WebUSB is my favorite browser API
WebUSB is an API that allows web applications to communicate directly with USB devices, bypassing the need for native drivers or middleware. As you might imagine, this opens up a universe of new possibilities for developers, making hardware more accessible than ever through the web.
My experience
The first time I interacted with WebUSB was around 2017, when Chrome shipped its first implementation.
At the time, I was developing a web-based point-of-sales app that was able to connect to a thermal receipt printer over local network. However, the network conditions in the field could be kinda unstable, leading to frustrations and many after-hours support calls. And that lead me to an investigation into USB connectivity. Since we were web-based, I really only had 2 options:
- Create an electron wrapper that was able to connect with USB devices or - god forbid - create a native app
- Figure out how to make it work in the browser
And after some research I landed on WebUSB.
Using the API
The API is very simple to use, as the complexity is abstracted away by the browser. At its core, WebUSB leverages the browser as a bridge to the USB devices. With just a few lines of JavaScript, you can start talking to hardware directly from your webpage.
First, you need to request a device from the browser, and you can do so after the user interacted with an element.
navigator.usb.requestDevice()
.then(device => {
console.log(device.productName);
console.log(device.manufacturerName);
})
.catch(error => { console.log(error); });
This will prompt the user to select a device and you can optionally filter the list that is presented to the user. Once the user selects a device, you can then claim the necessary interfaces, and start interacting with them.
A device may have multiple interfaces, like a printing or a scanning interface, so in this example we loop over every available interface and ‘claim’ it. Finally we write
navigator.usb.requestDevice()
.then(async device => {
await device.open();
for (const config of device.configurations) {
for (const iface of config.interfaces) {
if (!iface.claimed) {
await device.claimInterface(iface.interfaceNumber);
}
}
}
const data = 'YOUR DATA';
const dataBuffer = new TextEncoder().encode(data);
await device.transferOut(1, dataBuffer);
})
.catch(error => { console.log(error); });
WebUSB requires a secure context for operation, which means your website must be served over HTTPS, with localhost being the exception.
Use-cases
Using USB devices in a browser through WebUSB sound like a great idea, but what can we do with it?
I’ve been using WebUSB for communicating with ESC/POS compatible thermal printers in point-of-sales applications on Android tablets and Windows computers. And there are several other practical use-cases, like the programming of Arduino boards, or even doing a device firmware upgrade - all from within a web-page, without drivers.
WebBluetooth
Webbluetooth is conceptually similar to WebUSB, but it focuses on Bluetooth devices instead of USB devices. The connectivity method is slightly different, as the browser has to deal with Bluetooth discovery & pairing.
Challenges & considerations
Even though WebUSB and WebBluetooth are not part of the official W3C spec, I’ve been using this API in production since 2017 without any major issues.
Both of these specs are developed and maintained through the WICG (web incubator community group) https://wicg.github.io/webusb/
Despite its potential, WebUSB still comes with its set of challenges in 2024, such as limited browser compatibility, bugs and quirks. One specific quirk on Windows devices is that for some devices you need to replace the driver to WinUSB using a tool called Zadig for them to be usable by browsers. This in turn can potentially break other integrations that do rely on the driver.
In conclusion
In conclusion, WebUSB saved me tons of development time over the past years and I’m happily introducing it into new projects where I can.