Glossary

Web Bluetooth

Web Bluetooth is a browser API, exposed as navigator.bluetooth, that lets a web page connect to a nearby Bluetooth Low Energy device and read or write its GATT characteristics. It is a Web Bluetooth Community Group draft, not a W3C Recommendation, and it works only in secure contexts. MDN marks it experimental and not Baseline, so check browser support before relying on it.

How it works

Everything starts with navigator.bluetooth.requestDevice(options), which shows a browser chooser. The user must pick a device, and the call needs a user gesture. Your options must include filters, such as a list of services, or set acceptAllDevices to true. A site that uses acceptAllDevices can only touch services it also lists in optionalServices.

After the user picks a device, the typical sequence is:

  • device.gatt.connect() opens a GATT connection and resolves with a server object.
  • server.getPrimaryService(name) returns a service, such as one named heart_rate.
  • service.getCharacteristic(name) returns a characteristic.
  • characteristic.readValue() reads it, writeValueWithResponse() writes it, and startNotifications() subscribes to updates delivered as characteristicvaluechanged events.

A single written value cannot exceed 512 bytes, the maximum attribute value length. A longer buffer rejects with InvalidModificationError. Writing while disconnected rejects with NetworkError.

The spec keeps a blocklist of GATT services, characteristics and manufacturer data that are unsafe to expose. Using a blocklisted one rejects with SecurityError.

Services are identified by UUIDs. Bluetooth shortens them to 16 or 32 bits by replacing the top 32 bits of a fixed 128-bit base value, 00000000-0000-1000-8000-00805f9b34fb. The spec's own BluetoothUUID.canonicalUUID() does this, and the sample below re-implements it. The spec requires full UUID strings to be lowercase, with no short forms.

const BASE = "00000000-0000-1000-8000-00805f9b34fb";
function canonicalUUID(alias) {
  return alias.toString(16).padStart(8, "0") + BASE.slice(8);
}
console.log(canonicalUUID(0xDEADBEEF));
console.log(canonicalUUID(0x1234));
console.log(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(canonicalUUID(0x1234)));
console.log(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test("180D"));

Output:

deadbeef-0000-1000-8000-00805f9b34fb
00001234-0000-1000-8000-00805f9b34fb
true
false

Why is a Web Bluetooth service missing after connecting?

A service is missing after connecting when it was not named in filters or optionalServices during requestDevice(). Access is granted only for the services you listed in that call. Add the service UUID to optionalServices, request the device again, and the service will appear.

Common pitfalls

  • Using a 16-bit string as a UUID: a raw "180D" is not a valid UUID string and fails the lowercase 8-4-4-4-12 pattern. Pass a GATT name, a number such as 0x180D, or the full 128-bit form.
  • Calling requestDevice() without a click: the chooser needs transient user activation, so run it from a button handler.
  • Forgetting optionalServices: you can connect but then cannot reach services that were not declared in the request.
  • Writing past 512 bytes: split the data into smaller writes, since longer buffers reject with InvalidModificationError.
  • Assuming the connection survives: devices drop out of range. Listen for gattserverdisconnected and reconnect, and call startNotifications() again afterward.
  • Expecting it everywhere: the API is limited to some browsers, so feature-detect navigator.bluetooth.

Related terms

  • HTTPS — the secure context the API requires
  • Permissions-Policy header — its bluetooth directive defaults to same-origin frames only
  • WebUSB — the sibling API for wired USB devices
  • UUID — the 128-bit identifier format that service names expand to
  • DOM — the document model that fires the characteristic events

See also

  • Term: UUID — explains the 128-bit format behind Bluetooth service identifiers