How Web Bluetooth Enables BLE Communication in Chromium

This article explains how the Web Bluetooth API in the Chromium web browser allows websites to directly communicate with nearby Bluetooth Low Energy (BLE) peripherals. It covers the strict security and user-permission protocols required to establish a connection, the role of the Generic Attribute Profile (GATT) in structuring data, the step-by-step process of reading and writing data using JavaScript, and how Chromium bridges the gap between web code and the operating system's native Bluetooth stack.

Security and User Gesture Requirements

To protect user privacy and security, Chromium enforces strict rules before any Bluetooth communication can occur. First, the Web Bluetooth API is only available in secure contexts, meaning the website must be served over HTTPS (or localhost during development). Second, a website cannot automatically scan for or connect to nearby BLE devices upon loading. The communication process must be initiated by a deliberate user gesture, such as a mouse click or a screen tap.

Device Discovery and the Permission Prompt

Once the user triggers an action, the website calls the navigator.bluetooth.requestDevice() JavaScript method. Developers pass filters into this method to specify what kind of devices the website wants to communicate with, based on device names, service UUIDs (Universally Unique Identifiers), or manufacturer data.

Chromium intercepts this API call and displays a native browser prompt to the user. This prompt scans for and lists nearby BLE peripherals that match the developer's filters. The user must manually select a device from this list and click "Pair" to grant the website permission to access that specific peripheral. This prevents malicious websites from silently tracking users via nearby Bluetooth beacons.

The GATT Architecture

The Web Bluetooth API relies entirely on the Generic Attribute Profile (GATT) to structure data exchange. Once a peripheral is connected, Chromium interacts with it as a GATT Server. GATT organizes data hierarchically into Services and Characteristics:

  • Services: Logical groups of data that accomplish a specific function on the device (e.g., a "Heart Rate Service" or a "Battery Service").
  • Characteristics: Specific data points contained within a service (e.g., the actual heart rate measurement or the current battery percentage).

Every service and characteristic is identified by a standardized 16-bit or custom 128-bit UUID. To prevent websites from accessing sensitive system hardware, Chromium maintains a blocklist of restricted UUIDs (such as Human Interface Devices like Bluetooth keyboards) that the Web Bluetooth API is forbidden from accessing.

Connecting and Transferring Data

After the user selects a device from the Chromium prompt, the API returns a JavaScript Promise that resolves with a BluetoothDevice object. The website then connects to the device's GATT server using the device.gatt.connect() method.

Once the connection is established, the website traverses the GATT hierarchy using asynchronous JavaScript Promises to interact with the hardware:

  1. Get the Service: The website requests the target service using its UUID via server.getPrimaryService().
  2. Get the Characteristic: From that service, the website requests the desired characteristic via service.getCharacteristic().
  3. Read Data: The website can read the characteristic's current state by calling characteristic.readValue(). This returns a JavaScript DataView object containing raw byte data, which the website can parse into usable information.
  4. Write Data: The website can send commands or data to the peripheral by passing an ArrayBuffer or DataView of bytes into the characteristic.writeValue() method.
  5. Subscribe to Notifications: For continuous data streams, such as a fitness tracker broadcasting real-time telemetry, the website calls characteristic.startNotifications(). It then listens for the characteristicvaluechanged event to process incoming data packets automatically as the peripheral sends them.

Chromium's OS Abstraction Layer

Behind the scenes, Chromium acts as a translator between the web page's sandboxed JavaScript environment and the host operating system's native Bluetooth stack. Because Windows, macOS, Linux, Android, and ChromeOS all manage Bluetooth hardware differently, Chromium utilizes an internal C++ abstraction layer.

When a website executes a Web Bluetooth API command, the Chromium renderer process sends an Inter-Process Communication (IPC) message to the main browser process. The browser process validates the security permissions, translates the JavaScript request into the appropriate OS-level Bluetooth API call (such as CoreBluetooth on macOS, WinRT on Windows, or BlueZ on Linux), and routes the hardware's response back to the web page as a standard JavaScript Promise.