The Web Bluetooth API allows websites to communicate with Bluetooth devices.


![François Beaufort](https://web.dev/images/authors/beaufortfrancois.jpg) François Beaufort [GitHub](https://github.com/beaufortfrancois)

<br />

What if I told you websites could communicate with nearby Bluetooth devices
in a secure and privacy-preserving way? This way, heart rate monitors, singing
lightbulbs, and even [turtles](https://www.youtube.com/watch?v=1LV1Fk5ZXwA) could interact directly with a website.

Until now, the ability to interact with Bluetooth devices has been possible
only for platform-specific apps. The Web Bluetooth API aims to change this and
brings it to web browsers as well.

## Before we start

This document assumes you have some basic knowledge of how Bluetooth Low
Energy (BLE) and the [Generic Attribute Profile](https://developer.chrome.com/docs/capabilities/bluetooth/GATT) work.

Even though the [Web Bluetooth API specification](https://webbluetoothcg.github.io/web-bluetooth/) is not finalized yet, the spec
authors are actively looking for enthusiastic developers to try out this API and
give [feedback on the spec](https://github.com/WebBluetoothCG/web-bluetooth/issues) and [feedback on the implementation](https://issues.chromium.org/issues/new?noWizard=true&template=1963519&component=1456303).

A subset of the Web Bluetooth API is available in ChromeOS, Chrome for Android
6.0, Mac (Chrome 56) and Windows 10 (Chrome 70). This means you should be able
to [request](https://developer.chrome.com/docs/capabilities/bluetooth#request) and [connect to](https://developer.chrome.com/docs/capabilities/bluetooth#connect) nearby Bluetooth Low Energy devices,
[read](https://developer.chrome.com/docs/capabilities/bluetooth#read)/[write](https://developer.chrome.com/docs/capabilities/bluetooth#write) Bluetooth characteristics, [receive GATT Notifications](https://developer.chrome.com/docs/capabilities/bluetooth#notifications), know
when a [Bluetooth device gets disconnected](https://developer.chrome.com/docs/capabilities/bluetooth#disconnect), and even [read and write to
Bluetooth descriptors](https://developer.chrome.com/docs/capabilities/bluetooth#descriptors). See MDN's [Browser compatibility](https://developer.mozilla.org/docs/Web/API/Web_Bluetooth_API#Browser_compatibility) table for more
information.

For Linux and earlier versions of Windows, enable the
`#experimental-web-platform-features` flag in `about://flags`.

> [!NOTE]
> **Note:** For Bluetooth Classic devices exposing an RFCOMM service, consider using the Web Serial API. Refer to our guide on [Serial over Bluetooth on the web](https://developer.chrome.com/blog/serial-over-bluetooth) for more information.

### Available for origin trials

In order to get as much feedback as possible from developers using the Web
Bluetooth API in the field, Chrome has previously added this feature in Chrome
53 as an [origin trial](https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md) for ChromeOS, Android, and Mac.

The trial has successfully ended in January 2017.

## Security requirements

To understand the security tradeoffs, I recommend the [Web Bluetooth Security
Model](https://medium.com/@jyasskin/the-web-bluetooth-security-model-666b4e7eed2) post from Jeffrey Yasskin, a software engineer on the Chrome team,
working on the Web Bluetooth API specification.

### HTTPS only

Because this experimental API is a powerful new feature added to the web, it is
made available only to [secure contexts](https://w3c.github.io/webappsec-secure-contexts/#intro). This means you'll need to build with
[TLS](https://en.wikipedia.org/wiki/Transport_Layer_Security) in mind.

### User gesture required

As a security feature, discovering Bluetooth devices with
`navigator.bluetooth.requestDevice` must be triggered by [a user gesture](https://html.spec.whatwg.org/multipage/interaction.html#activation) such
as a touch or a mouse click. We're talking about listening to
[`pointerup`](https://developer.chrome.com/blog/pointer-events), `click`, and `touchend` events.

    button.addEventListener('pointerup', function(event) {
      // Call navigator.bluetooth.requestDevice
    });

## Get into the code

The Web Bluetooth API relies heavily on JavaScript [Promises](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise). If you're not
familiar with them, check out this great [Promises tutorial](https://developer.chrome.com/promises). One more thing,
`() => {}` are ECMAScript 2015 [Arrow functions](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions).

### Request Bluetooth devices

This version of the Web Bluetooth API specification allows websites, running in
the Central role, to connect to remote GATT Servers over a BLE connection. It
supports communication among devices that implement Bluetooth 4.0 or later.

When a website requests access to nearby devices using
`navigator.bluetooth.requestDevice`, the browser prompts user with a device
chooser where they can pick one device or cancel the request.
[Bluetooth device user prompt.](https://webbluetoothcg.github.io/demos/playbulb-candle/)

The `navigator.bluetooth.requestDevice()` function takes a mandatory object that
defines filters. These filters are used to return only devices that match some
advertised Bluetooth GATT services and/or the device name.

#### Services filter

For example, to request Bluetooth devices advertising the [Bluetooth GATT
Battery Service](https://www.bluetooth.com/specifications/gatt/):

    navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

If your Bluetooth GATT Service is not on the list of [the standardized Bluetooth
GATT services](https://www.bluetooth.com/specifications/assigned-numbers/) though, you may provide either the full Bluetooth UUID or a short
16- or 32-bit form.

    navigator.bluetooth.requestDevice({
      filters: [{
        services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
      }]
    })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

#### Name filter

You can also request Bluetooth devices based on the device name being advertised
with the `name` filters key, or even a prefix of this name with the `namePrefix`
filters key. Note that in this case, you will also need to define the
`optionalServices` key to be able to access any services not included in a
service filter. If you don't, you'll get an error later when trying to access
them.

    navigator.bluetooth.requestDevice({
      filters: [{
        name: 'Francois robot'
      }],
      optionalServices: ['battery_service'] // Required to access service later.
    })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

#### Manufacturer data filter

It is also possible to request Bluetooth devices based on the manufacturer
specific data being advertised with the `manufacturerData` filters key. This key
is an array of objects with a mandatory [Bluetooth company identifier](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/) key named
`companyIdentifier`. You can also provide a data prefix that filters
manufacturer data from Bluetooth devices that start with it. Note that you will
also need to define the `optionalServices` key to be able to access any services
not included in a service filter. If you don't, you'll get an error later when
trying to access them.

    // Filter Bluetooth devices from Google company with manufacturer data bytes
    // that start with [0x01, 0x02].
    navigator.bluetooth.requestDevice({
      filters: [{
        manufacturerData: [{
          companyIdentifier: 0x00e0,
          dataPrefix: new Uint8Array([0x01, 0x02])
        }]
      }],
      optionalServices: ['battery_service'] // Required to access service later.
    })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

A mask can also be used with a data prefix to match some patterns in
manufacturer data. Check out the [Bluetooth data filters explainer](https://github.com/WebBluetoothCG/web-bluetooth/blob/main/data-filters-explainer.md) to learn
more.

> [!NOTE]
> **Note:** At the time of writing, the `manufacturerData` filter key is available in Chrome 92. If backwards compatibility with older browsers is desired, you have to provide a fallback option as the manufacturer data filter is considered empty. See an \[example\].

#### Exclusion filters

The `exclusionFilters` option in `navigator.bluetooth.requestDevice()` lets
you exclude some devices from the browser picker. It can be used to exclude
devices that match a broader filter but are unsupported.

    // Request access to a bluetooth device whose name starts with "Created by".
    // The device named "Created by Francois" has been reported as unsupported.
    navigator.bluetooth.requestDevice({
      filters: [{
        namePrefix: "Created by"
      }],
      exclusionFilters: [{
        name: "Created by Francois"
      }],
      optionalServices: ['battery_service'] // Required to access service later.
    })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

> [!NOTE]
> **Note:** The `exclusionFilters` option is available in Chrome 114.

#### No filters

Finally, instead of `filters` you can use the `acceptAllDevices` key to show all
nearby Bluetooth devices. You will also need to define the `optionalServices`
key to be able to access some services. If you don't, you'll get an error later
when trying to access them.

    navigator.bluetooth.requestDevice({
      acceptAllDevices: true,
      optionalServices: ['battery_service'] // Required to access service later.
    })
    .then(device => { /* ... */ })
    .catch(error => { console.error(error); });

> [!CAUTION]
> **Caution:** This may result in a bunch of unrelated devices being shown in the chooser and energy being wasted as there are no filters. Use it with caution.

### Connect to a Bluetooth device

So what do you do now that you have a `BluetoothDevice`? Let's connect to the
Bluetooth remote GATT Server which holds the service and characteristic
definitions.

    navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
    .then(device => {
      // Human-readable name of the device.
      console.log(device.name);

      // Attempts to connect to remote GATT Server.
      return device.gatt.connect();
    })
    .then(server => { /* ... */ })
    .catch(error => { console.error(error); });

### Read a Bluetooth Characteristic

Here we connect to the GATT Server of the remote Bluetooth device. Now we
want to get a Primary GATT Service and read a characteristic that belongs to
this service. Let's try, for instance, to read the current charge level of the
device's battery.

In the example ahead, `battery_level` is the [standardized Battery Level
Characteristic](https://www.bluetooth.com/specifications/gatt/).

    navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
    .then(device => device.gatt.connect())
    .then(server => {
      // Getting Battery Service...
      return server.getPrimaryService('battery_service');
    })
    .then(service => {
      // Getting Battery Level Characteristic...
      return service.getCharacteristic('battery_level');
    })
    .then(characteristic => {
      // Reading Battery Level...
      return characteristic.readValue();
    })
    .then(value => {
      console.log(`Battery percentage is ${value.getUint8(0)}`);
    })
    .catch(error => { console.error(error); });

If you use a custom Bluetooth GATT characteristic, you may provide either the
full Bluetooth UUID or a short 16- or 32-bit form to
`service.getCharacteristic`.

Note that you can also add a `characteristicvaluechanged` event listener on a
characteristic to handle reading its value. Check out the [Read Characteristic
Value Changed Sample](https://googlechrome.github.io/samples/web-bluetooth/read-characteristic-value-changed.html) to see how to optionally handle upcoming GATT
notifications as well.

    ...
    .then(characteristic => {
      // Set up event listener for when characteristic value changes.
      characteristic.addEventListener('characteristicvaluechanged',
                                      handleBatteryLevelChanged);
      // Reading Battery Level...
      return characteristic.readValue();
    })
    .catch(error => { console.error(error); });

    function handleBatteryLevelChanged(event) {
      const batteryLevel = event.target.value.getUint8(0);
      console.log('Battery percentage is ' + batteryLevel);
    }

### Write to a Bluetooth Characteristic

Writing to a Bluetooth GATT Characteristic is as easy as reading it. This time,
let's use the Heart Rate Control Point to reset the value of the Energy Expended
field to 0 on a heart rate monitor device.

I promise there is no magic here. It's all explained in the [Heart Rate Control
Point Characteristic page](https://www.bluetooth.com/specifications/gatt/).

    navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('heart_rate'))
    .then(service => service.getCharacteristic('heart_rate_control_point'))
    .then(characteristic => {
      // Writing 1 is the signal to reset energy expended.
      const resetEnergyExpended = Uint8Array.of(1);
      return characteristic.writeValue(resetEnergyExpended);
    })
    .then(_ => {
      console.log('Energy expended has been reset.');
    })
    .catch(error => { console.error(error); });

### Receive GATT notifications

Now, let's see how to be notified when the [Heart Rate Measurement](https://www.bluetooth.com/specifications/gatt/)
characteristic changes on the device:

    navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('heart_rate'))
    .then(service => service.getCharacteristic('heart_rate_measurement'))
    .then(characteristic => characteristic.startNotifications())
    .then(characteristic => {
      characteristic.addEventListener('characteristicvaluechanged',
                                      handleCharacteristicValueChanged);
      console.log('Notifications have been started.');
    })
    .catch(error => { console.error(error); });

    function handleCharacteristicValueChanged(event) {
      const value = event.target.value;
      console.log('Received ' + value);
      // TODO: Parse Heart Rate Measurement value.
      // See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
    }

The [Notifications Sample](https://googlechrome.github.io/samples/web-bluetooth/notifications.html) shows you to how to stop notifications with
`stopNotifications()` and properly remove the added `characteristicvaluechanged`
event listener.

### Disconnect from a Bluetooth Device

To provide a better user experience, you may want to listen for disconnection events
and invite the user to reconnect:

    navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
    .then(device => {
      // Set up event listener for when device gets disconnected.
      device.addEventListener('gattserverdisconnected', onDisconnected);

      // Attempts to connect to remote GATT Server.
      return device.gatt.connect();
    })
    .then(server => { /* ... */ })
    .catch(error => { console.error(error); });

    function onDisconnected(event) {
      const device = event.target;
      console.log(`Device ${device.name} is disconnected.`);
    }

You can also call `device.gatt.disconnect()` to disconnect your web app from the
Bluetooth device. This will trigger existing `gattserverdisconnected` event
listeners. Note that it will NOT stop bluetooth device communication if another
app is already communicating with the Bluetooth device. Check out the [Device
Disconnect Sample](https://googlechrome.github.io/samples/web-bluetooth/device-disconnect.html) and the [Automatic Reconnect Sample](https://googlechrome.github.io/samples/web-bluetooth/automatic-reconnect.html) to dive deeper.

> [!CAUTION]
> **Caution:** Bluetooth GATT attributes, services, characteristics, etc. are invalidated when a device disconnects. This means your code should always retrieve (through `getPrimaryService(s)`, `getCharacteristic(s)`, etc.) these attributes after reconnecting.

### Read and write to Bluetooth descriptors

Bluetooth GATT descriptors are attributes that describe a characteristic value.
You can read and write them to in a similar way to Bluetooth GATT
characteristics.

Let's see for instance how to read the user description of the measurement
interval of the device's health thermometer.

In the example below, `health_thermometer` is the [Health Thermometer service](https://www.bluetooth.com/specifications/gatt/),
`measurement_interval` the [Measurement Interval characteristic](https://www.bluetooth.com/specifications/gatt/), and
`gatt.characteristic_user_description` the [Characteristic User Description
descriptor](https://www.bluetooth.com/specifications/assigned-numbers/).

    navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('health_thermometer'))
    .then(service => service.getCharacteristic('measurement_interval'))
    .then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
    .then(descriptor => descriptor.readValue())
    .then(value => {
      const decoder = new TextDecoder('utf-8');
      console.log(`User Description: ${decoder.decode(value)}`);
    })
    .catch(error => { console.error(error); });

Now that we've read the user description of the measurement interval of the
device's health thermometer, let's see how to update it and write a custom
value.

    navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
    .then(device => device.gatt.connect())
    .then(server => server.getPrimaryService('health_thermometer'))
    .then(service => service.getCharacteristic('measurement_interval'))
    .then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
    .then(descriptor => {
      const encoder = new TextEncoder('utf-8');
      const userDescription = encoder.encode('Defines the time between measurements.');
      return descriptor.writeValue(userDescription);
    })
    .catch(error => { console.error(error); });

## Samples, demos and codelabs

All [Web Bluetooth samples](https://googlechrome.github.io/samples/web-bluetooth/index.html) below have been successfully tested. To enjoy these
samples to their fullest, I recommend you install the \[BLE Peripheral Simulator
Android App\] which simulates a BLE peripheral with a Battery Service, a Heart Rate
Service, or a Health Thermometer Service.

### Beginner

- [Device Info](https://googlechrome.github.io/samples/web-bluetooth/device-info.html) - retrieve basic device information from a BLE Device.
- [Battery Level](https://googlechrome.github.io/samples/web-bluetooth/battery-level.html) - retrieve battery information from a BLE Device advertising Battery information.
- [Reset Energy](https://googlechrome.github.io/samples/web-bluetooth/reset-energy.html) - reset energy expended from a BLE Device advertising Heart Rate.
- [Characteristic Properties](https://googlechrome.github.io/samples/web-bluetooth/characteristic-properties.html) - display all properties of a specific characteristic from a BLE Device.
- [Notifications](https://googlechrome.github.io/samples/web-bluetooth/notifications.html) - start and stop characteristic notifications from a BLE Device.
- [Device Disconnect](https://googlechrome.github.io/samples/web-bluetooth/device-disconnect.html) - disconnect and get notified from a disconnection of a BLE Device after connecting to it.
- [Get Characteristics](https://googlechrome.github.io/samples/web-bluetooth/get-characteristics.html) - get all characteristics of an advertised service from a BLE Device.
- [Get Descriptors](https://googlechrome.github.io/samples/web-bluetooth/get-descriptors.html) - get all characteristics' descriptors of an advertised service from a BLE Device.
- [Manufacturer Data Filter](https://googlechrome.github.io/samples/web-bluetooth/manufacturer-data-filter.html) - retrieve basic device information from a BLE Device that matches manufacturer data.
- [Exclusion Filters](https://googlechrome.github.io/samples/web-bluetooth/exclusion-filters.html) - retrieve basic device information from a BLE Device featuring basic exclusion filters.

### Combining multiple operations

- [GAP Characteristics](https://googlechrome.github.io/samples/web-bluetooth/gap-characteristics.html) - get all GAP characteristics of a BLE Device.
- [Device Information Characteristics](https://googlechrome.github.io/samples/web-bluetooth/device-information-characteristics.html) - get all Device Information characteristics of a BLE Device.
- [Link Loss](https://googlechrome.github.io/samples/web-bluetooth/link-loss.html) - set the Alert Level characteristic of a BLE Device (readValue \& writeValue).
- [Discover Services \& Characteristics](https://googlechrome.github.io/samples/web-bluetooth/discover-services-and-characteristics.html) - discover all accessible primary services and their characteristics from a BLE Device.
- [Automatic Reconnect](https://googlechrome.github.io/samples/web-bluetooth/automatic-reconnect.html) - reconnect to a disconnected BLE device using an exponential backoff algorithm.
- [Read Characteristic Value Changed](https://googlechrome.github.io/samples/web-bluetooth/read-characteristic-value-changed.html) - read battery level and be notified of changes from a BLE Device.
- [Read Descriptors](https://googlechrome.github.io/samples/web-bluetooth/read-descriptors.html) - read all characteristic's descriptors of a service from a BLE Device.
- [Write Descriptor](https://googlechrome.github.io/samples/web-bluetooth/write-descriptor.html) - write to the descriptor "Characteristic User Description" on a BLE Device.

Check out our [curated Web Bluetooth Demos](https://github.com/WebBluetoothCG/demos) and [official Web Bluetooth Codelabs](https://github.com/search?q=org:googlecodelabs+bluetooth) as well.

## Libraries

- [web-bluetooth-utils](https://www.npmjs.com/package/web-bluetooth-utils) is a npm module that adds some convenience functions to the API.
- A Web Bluetooth API shim is available in [noble](https://github.com/sandeepmistry/noble), the most popular Node.js BLE central module. This allows you to webpack/browserify noble without the need for a WebSocket server or other plugins.
- [angular-web-bluetooth](https://github.com/manekinekko/angular-web-bluetooth) is a module for [Angular](https://angularjs.org) that abstracts away all the boilerplate needed to configure the Web Bluetooth API.

## Tools

- [Get Started with Web Bluetooth](https://beaufortfrancois.github.io/sandbox/web-bluetooth/generator) is a simple Web App that will generate all the JavaScript boilerplate code to start interacting with a Bluetooth device. Enter a device name, a service, a characteristic, define its properties and you're good to go.
- If you're already a Bluetooth developer, the [Web Bluetooth Developer Studio
  Plugin](https://github.com/beaufortfrancois/sandbox/tree/gh-pages/web-bluetooth/bluetooth-developer-studio-plugin) will also generate the Web Bluetooth JavaScript code for your Bluetooth device.

## Tips

A **Bluetooth Internals** page is available in Chrome at
`about://bluetooth-internals` so that you can inspect everything about nearby
Bluetooth devices: status, services, characteristics, and descriptors.
![Screenshot of the internal page to debug Bluetooth in Chrome](https://developer.chrome.com/static/docs/capabilities/bluetooth/image/screenshot-the-internal-acdfb455a1a38.jpg) Internal page in Chrome for debugging Bluetooth devices.

I also recommend checking out the official [How to file Web Bluetooth bugs](https://sites.google.com/a/chromium.org/dev/developers/how-tos/file-web-bluetooth-bugs)
page as debugging Bluetooth can be hard sometimes.

> [!CAUTION]
> **Caution:** Reading and writing to Bluetooth characteristics in parallel may raise errors depending on the platform. I strongly suggest you manually queue GATT operation requests when appropriate. See ["GATT operation in progress - how to handle
> it?"](https://github.com/WebBluetoothCG/web-bluetooth/issues/188).

## What's next

Check the [browser and platform implementation status](https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md) first to know which parts
of the Web Bluetooth API are currently being implemented.

Though it's still incomplete, here's a sneak peek of what to expect in the near
future:

- [Scanning for nearby BLE advertisements](https://github.com/WebBluetoothCG/web-bluetooth/pull/239) will happen with `navigator.bluetooth.requestLEScan()`.
- A new `serviceadded` event will track newly discovered Bluetooth GATT Services while `serviceremoved` event will track removed ones. A new `servicechanged` event will fire when any characteristic and/or descriptor gets added or removed from a Bluetooth GATT Service.

### Show support for the API

Are you planning to use the Web Bluetooth API? Your public support helps the Chrome team
prioritize features and shows other browser vendors how critical it is to support them.

Send a tweet to [@ChromiumDev](https://twitter.com/ChromiumDev) using the hashtag
[`#WebBluetooth`](https://twitter.com/search?q=%23WebBluetooth&src=typed_query&f=live)
and let us know where and how you are using it.

## Resources

- [Stack Overflow](https://stackoverflow.com/questions/tagged/web-bluetooth)
- [Chrome Feature Status](https://www.chromestatus.com/feature/5264933985976320)
- [Chrome Implementation bugs](https://crbug.com/?q=component:Blink%3EBluetooth)
- [Web Bluetooth spec](https://webbluetoothcg.github.io/web-bluetooth)
- [Spec Issues on GitHub](https://github.com/WebBluetoothCG/web-bluetooth/issues)
- [BLE Peripheral Simulator App](https://github.com/WebBluetoothCG/ble-test-peripheral-android)

[Video](https://www.youtube.com/watch?v=_BUwOBdLjzQ)

## Acknowledgements

Thanks to [Kayce Basques](https://github.com/kaycebasques) for reviewing.