WebUSB API: How JavaScript Talks to USB Devices
The WebUSB API is a modern web standard that allows web applications to communicate directly with Universal Serial Bus (USB) devices from within the browser. Traditionally, hardware communication required native applications, specialized software development kits (SDKs), or dedicated system drivers. WebUSB eliminates these barriers by providing a secure, standard JavaScript interface to identify, connect, and transfer data to compatible hardware. This article explains how WebUSB works, the underlying communication process in JavaScript, its security model, and common practical applications.
Understanding the WebUSB API
WebUSB bridges the gap between web platforms and physical hardware. Built primarily for Chromium-based browsers, it enables web pages to discover and interact with non-standard USB devices—such as microcontrollers, scientific instruments, receipt printers, and custom electronics—without requiring end-users to install specific desktop drivers.
Standard peripherals like keyboards, mice, and mass storage devices are generally blocked by the browser to protect core system input/output mechanisms. WebUSB targets custom and specialized devices that utilize vendor-specific interfaces.
How JavaScript Communicates with USB Devices
Communication between a web page and a USB device follows a standardized lifecycle: discovery, connection, interface configuration, and data transfer.
1. Requesting Device Access
For security reasons, a website cannot scan for connected USB devices automatically. Access must be initiated by an explicit user gesture, such as clicking a button.
JavaScript calls the navigator.usb.requestDevice()
method, passing filters that specify vendor IDs (VID) and product IDs
(PID):
const connectButton = document.querySelector('#connect');
connectButton.addEventListener('click', async () => {
try {
const device = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x2341 }] // Example: Arduino Vendor ID
});
console.log(`Connected to: ${device.productName}`);
} catch (error) {
console.error('Connection failed:', error);
}
});2. Opening and Configuring the Device
Once the user selects a device from the browser prompt and grants permission, JavaScript opens a session, selects an active configuration, and claims the target interface:
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);Claiming an interface ensures exclusive communication rights with that specific capability of the hardware.
3. Transferring Data
Data exchange occurs via standard USB transfer types using endpoints:
- Control Transfers: Used for configuration, status
checks, and commands (
device.controlTransferIn(),device.controlTransferOut()). - Bulk Transfers: Used for large, non-time-critical
data like firmware updates (
device.transferIn(),device.transferOut()). - Interrupt Transfers: Used for small, time-sensitive data such as sensor readings.
- Isochronous Transfers: Used for real-time streaming data where occasional packet loss is acceptable (e.g., audio/video).
Sending data to an endpoint:
const encoder = new TextEncoder();
const data = encoder.encode('HELLO_DEVICE');
await device.transferOut(1, data); // Sends data to endpoint 1Receiving data from an endpoint:
const result = await device.transferIn(1, 64); // Reads up to 64 bytes from endpoint 1
const decoder = new TextDecoder();
const message = decoder.decode(result.data);
console.log(`Received: ${message}`);4. Closing the Connection
When communication is complete, the application releases the claimed interfaces and closes the device session:
await device.releaseInterface(0);
await device.close();Security and Permissions
WebUSB implements strict security constraints to prevent malicious hardware access:
- Secure Contexts Only: WebUSB only runs on origins
served over HTTPS (or
localhostfor development). - Explicit User Consent: Sites cannot silently access hardware; users must choose the device from a native browser modal.
- Feature Policy: Permissions can be controlled via
the Permissions Policy header (
usb), preventing third-party iframes from accessing USB without authorization. - OS-Level Driver Protection: If an operating system driver has already claimed an interface (like an HID mouse), the browser cannot access it.
Common Use Cases
- Web-Based IDEs and Hardware Programming: Flashing firmware onto microcontrollers (such as Arduino, BBC micro:bit, or ESP32) directly from a browser-based IDE.
- Point-of-Sale Systems: Connecting thermal receipt printers, barcode scanners, or scales to browser-based retail applications.
- Diagnostics and Configuration: Configuring hardware parameters or reading logs from specialized industrial or medical instruments.