v157 · web bluetooth · needs hardware for the full path
Reconnect lifecycle
The spec marks device.gatt as [SameObject]: every read returns the same server instance, across disconnects and reconnects. Combined with the server now being an EventTarget, that yields the killer property of this design — attach a connection-level listener once and it survives every reconnect. This page proves the identity on real hardware and demonstrates the backoff-reconnect pattern that relies on it.
Prerequisites
probing…
Prove it on a device
Opens Chrome's device chooser. After connecting, use "Drop & auto-reconnect" (or power-cycle the device) and watch: the reconnect resolves the same server object (identity check runs each cycle), and the listener attached before the first drop keeps firing after reconnection without being re-attached.
- device.gatt === first ref
- —
- reconnect cycles
- 0
- listener re-attached?
- never (that's the point)
- server.connected
- —
Log
- Nothing yet.
the pattern
const device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
const server = device.gatt; // [SameObject] — stable for the device's life
// Attach ONCE. Survives every reconnect because the object survives.
server.addEventListener("maxwritewithoutresponsesizechanged", onSizeChange);
device.addEventListener("gattserverdisconnected", async () => {
for (let delay = 250; delay <= 8000; delay *= 2) { // exponential backoff
await new Promise((r) => setTimeout(r, delay));
try {
await server.connect(); // same object, fresh connection
return; // listener above is still live
} catch { /* next backoff step */ }
}
showOffline();
});
// The stale-listener bug this design prevents: with a NEW server object
// per connection you would re-attach listeners each time — forget one
// path and a renegotiated write size silently truncates your queue.