How WebUSB Connects USB Devices in JavaScript
The WebUSB API enables web applications to securely and directly communicate with Universal Serial Bus (USB) hardware peripherals using client-side JavaScript. Traditionally, hardware communication required platform-specific native drivers or standalone desktop software. WebUSB removes this barrier by providing a standardized JavaScript interface inside modern web browsers, allowing web pages to discover, configure, and exchange data with connected USB hardware after explicit user authorization.
The Security and Permission Model
Direct hardware access carries inherent security risks, so the WebUSB API enforces strict browser security controls before any data can be transferred:
- Secure Contexts (HTTPS): WebUSB is restricted to
secure origins (
https://orlocalhost) to prevent man-in-the-middle attacks. - User Gesture Requirement: A device prompt can only be triggered by an explicit user action, such as clicking a button or pressing a key.
- Explicit User Consent: The browser displays a native permissions dialog listing available devices matching predefined filters. The web page cannot access any device until the user explicitly selects it and clicks “Connect.”
- Protected Device Classes: WebUSB explicitly blocks access to standard human interface devices (like primary keyboards and mice), mass storage drives, and smart cards to prevent unauthorized input hijacking or system compromise.
Discovering and Requesting a Device
Communication begins by invoking
navigator.usb.requestDevice(). Developers supply filtering
criteria, such as the Vendor ID (vendorId) and Product ID
(productId), to narrow down the list of compatible hardware
shown in the browser prompt:
const filters = [{ vendorId: 0x1234, productId: 0x5678 }];
try {
const device = await navigator.usb.requestDevice({ filters });
console.log("Device selected:", device.productName);
} catch (error) {
console.error("No device selected or access denied:", error);
}Once granted, the browser retains permission, and previously paired
devices can be re-accessed in subsequent sessions via
navigator.usb.getDevices().
Opening and Configuring the Device Session
Obtaining a USBDevice object does not immediately allow
data transmission. The session must be opened, configured, and bound to
specific USB interfaces:
- Open Session: Call
await device.open()to initiate the physical session with the operating system’s USB stack. - Select Configuration: Call
await device.selectConfiguration(1)to activate the desired power and operational state defined in the USB device descriptors. - Claim Interface: Call
await device.claimInterface(0)to gain exclusive control over the interface that houses the target communication endpoints. Claiming an interface prevents other applications or operating system drivers from interacting with that specific component of the device.
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);Exchanging Data via Endpoints
USB hardware communicates through directional channels called endpoints. WebUSB supports all standard USB transfer types:
- Control Transfers (
controlTransferIn,controlTransferOut): Used primarily for device configuration, querying hardware parameters, or sending command packets. - Bulk Transfers (
transferIn,transferOut): Used for large or non-time-critical data transfers, such as transferring files, firmware updates, or serial data. - Interrupt Transfers: Used for low-latency, periodic communication, such as reading sensors or custom button controllers.
- Isochronous Transfers (
isochronousTransferIn,isochronousTransferOut): Used for real-time, time-sensitive streams (e.g., audio or video) where guaranteed delivery rate is more critical than error correction.
To send raw binary data (via ArrayBuffer or
TypedArray) to an OUT endpoint:
const data = new Uint8Array([0x01, 0x02, 0x03]);
await device.transferOut(1, data); // Sends data to endpoint number 1To receive incoming binary data from an IN endpoint:
const result = await device.transferIn(1, 64); // Listens on endpoint 1 for up to 64 bytes
const receivedData = new Uint8Array(result.data.buffer);Closing the Connection
When data exchange is complete, the application releases its
exclusive lock and terminates the session cleanly using
releaseInterface() and close():
await device.releaseInterface(0);
await device.close();By standardizing these discovery, authentication, configuration, and transfer steps into Promise-based JavaScript methods, the WebUSB API provides web applications with deterministic, low-level hardware control directly inside the browser.