WebUSB API: Connect USB Devices from the Browser

Enable direct communication between web applications and USB peripherals without native drivers. Learn how to build hardware-integrated web apps.

What is WebUSB?

The WebUSB API represents a significant leap forward in web capabilities, enabling direct communication between web applications and USB peripherals without requiring native drivers or middleware. This powerful API, developed through the Web Platform Incubator Community Group (WICG), allows hardware manufacturers to build cross-platform JavaScript SDKs for their devices and opens doors for developers to create innovative web-based hardware interactions.

For modern web development, WebUSB bridges the gap between web applications and physical hardware, making it possible to build point-of-sale systems, program microcontrollers, interact with industrial equipment, and perform device diagnostics--all from a secure browser environment.

Why WebUSB Matters for Web Developers

Key capabilities that transform what's possible in web applications

Cross-Platform Hardware Access

Build hardware-interacting applications that work consistently across any supporting browser without platform-specific code.

No Driver Installation

Users connect devices directly through the browser, eliminating the need for native driver downloads and system modifications.

Secure by Design

Built-in permission prompts, secure context requirements, and sandboxed execution protect users while enabling legitimate access.

Web Deployment Model

Inherit all advantages of web applications including automatic updates, cross-platform compatibility, and reduced friction.

Core WebUSB Architecture

The USB Interface

The foundation of WebUSB communication is the navigator.usb property, which provides access to the USB interface. This interface enables web applications to discover, request access to, and communicate with USB devices.

Key methods:

  • requestDevice() - Prompts users to select a device
  • getDevices() - Accesses previously authorized devices

The USBDevice Interface

Once connected, the USBDevice object provides metadata and control methods:

Metadata properties:

  • vendorId - Manufacturer identifier
  • productId - Product identifier
  • productName - Human-readable product name
  • manufacturerName - Manufacturer name

Essential methods:

  • open() - Initialize the USB session
  • close() - Release the connection
  • selectConfiguration() - Choose device configuration
  • claimInterface() - Claim an interface for exclusive use
  • transferIn() / transferOut() - Data exchange with endpoints
  • controlTransferIn() / controlTransferOut() - Control requests
Requesting Device Access
1// Request access to a specific device2navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })3 .then(device => {4 console.log(device.productName); // "Arduino Micro"5 console.log(device.manufacturerName); // "Arduino LLC"6 })7 .catch(error => {8 console.error(error);9 });

Connecting to a USB Device

Step 1: Requesting Device Access

The connection process begins with requesting access. The requestDevice() method triggers a browser prompt where users select devices matching your filters. This user-mediated selection is a core security protection.

async function connectToDevice() {
 try {
 const device = await navigator.usb.requestDevice({
 filters: [{
 vendorId: 0x2341,
 classCode: 0xFF,
 protocolCode: 0x01
 }]
 });
 return device;
 } catch (error) {
 console.error('Device selection cancelled:', error);
 throw error;
 }
}

Step 2: Opening and Initializing

After receiving a device object, explicitly open the connection:

async function openDevice(device) {
 await device.open();
 if (device.configuration === null) {
 await device.selectConfiguration(1);
 }
 return device;
}

Step 3: Claiming Interfaces

USB interfaces must be claimed for exclusive use:

async function claimInterfaces(device) {
 for (const config of device.configurations) {
 for (const iface of config.interfaces) {
 if (!iface.claimed) {
 await device.claimInterface(iface.interfaceNumber);
 }
 }
 }
}

Step 4: Performing Data Transfers

With interfaces claimed, communicate through endpoints:

// Send data to the device
async function sendData(device, endpointNumber, data) {
 const encoder = new TextEncoder();
 const buffer = encoder.encode(data);
 const result = await device.transferOut(endpointNumber, buffer);
 console.log(`Sent ${result.bytesWritten} bytes`);
}

// Receive data from the device
async function receiveData(device, endpointNumber, length = 64) {
 const result = await device.transferIn(endpointNumber, length);
 const decoder = new TextDecoder();
 const data = decoder.decode(result.data);
 return data;
}

USB Transfer Types

Understanding transfer types is essential for effective communication:

TypeUse CaseCharacteristics
ControlDevice configuration, status queriesRequired for all devices, bidirectional
BulkLarge data transfersHigh throughput, no timing guarantee
InterruptTime-sensitive dataSmall packets, guaranteed delivery
IsochronousStreaming (audio/video)Guaranteed bandwidth, may lose data

Security Model and Best Practices

Permission Architecture

WebUSB implements multi-layered security:

  1. Permission Prompt - Users explicitly select devices to grant access
  2. Secure Context - HTTPS required (localhost exception for development)
  3. Interface Claims - Only one context can claim an interface at a time
  4. Device Blocklist - Sensitive devices are protected from access

Best Practices for Production

Handle errors gracefully:

async function robustOperation(device, operation) {
 try {
 return await operation();
 } catch (error) {
 if (error.name === 'NotFoundError') {
 console.error('Device disconnected');
 } else if (error.name === 'SecurityError') {
 console.error('Permission denied');
 }
 throw error;
 }
}

Listen for connection events:

navigator.usb.addEventListener('connect', event => {
 console.log('Device connected:', event.device.productName);
});

navigator.usb.addEventListener('disconnect', event => {
 console.log('Device disconnected:', event.device.productName);
});

Request only necessary access: Use specific filters that match only the devices your application needs. This respects user privacy and increases the likelihood of granting access.

Educational & Scientific Devices

Integrate sensors, data acquisition tools, and measurement devices directly into online course materials for science education.

Point-of-Sale Systems

Connect receipt printers via USB for reliable local printing independent of network conditions in retail environments.

Device Programming & Updates

Build web-based tools for firmware updates, diagnostics, and configuration changes without requiring native software.

3D Printing & Manufacturing

Create web-based slicers and print management tools that send G-code directly to 3D printers through the browser.

Complete WebUSB Connection Example
1class USBPrinter {2 constructor(vendorId, productId) {3 this.vendorId = vendorId;4 this.productId = productId;5 this.device = null;6 }7 8 async connect() {9 try {10 this.device = await navigator.usb.requestDevice({11 filters: [{12 vendorId: this.vendorId,13 productId: this.productId14 }]15 });16 17 await this.device.open();18 if (this.device.configuration === null) {19 await this.device.selectConfiguration(1);20 }21 22 for (const config of this.device.configurations) {23 for (const iface of config.interfaces) {24 if (!iface.claimed) {25 await this.device.claimInterface(iface.interfaceNumber);26 }27 }28 }29 30 console.log('Printer connected:', this.device.productName);31 return true;32 } catch (error) {33 console.error('Connection failed:', error);34 return false;35 }36 }37 38 async print(text) {39 if (!this.device) throw new Error('Not connected');40 41 const encoder = new TextEncoder();42 const data = encoder.encode(text);43 44 // ESC/POS command to print and feed45 const escPosCommands = new Uint8Array([46 0x1B, 0x40, 0x1B, 0x61, 0x01 // Center alignment47 ]);48 49 await this.device.transferOut(1, escPosCommands);50 await this.device.transferOut(1, data);51 52 // Line feed53 const lineFeed = new Uint8Array([0x0A, 0x0A, 0x1D, 0x56, 0x00]);54 await this.device.transferOut(1, lineFeed);55 }56 57 async disconnect() {58 if (this.device) {59 await this.device.close();60 this.device = null;61 }62 }63}64 65// Usage66const printer = new USBPrinter(0x0416, 0x5011); // Common printer vendor/product67await printer.connect();68await printer.print('Hello from WebUSB!');69await printer.disconnect();

Conclusion

WebUSB enables web applications to communicate directly with USB peripherals, opening possibilities that were previously limited to native software. For developers building modern web applications, it enables:

  • Point-of-sale systems with reliable USB-connected peripherals
  • Educational tools integrating scientific measurement devices
  • Hardware programming and firmware update tools
  • Manufacturing applications for 3D printing and beyond

The API's design prioritizes security through user-mediated permission grants, secure context requirements, and thoughtful device protections. While browser support remains limited to Chromium-based browsers, the specification continues to mature.

For teams building applications that interact with hardware peripherals, WebUSB should be considered when the benefits of web deployment--cross-platform compatibility, automatic updates, reduced installation friction--align with project requirements. When combining WebUSB with AI automation, organizations can create sophisticated IoT solutions that leverage the browser as a universal interface for both software intelligence and hardware control.


Related Topics:

Frequently Asked Questions

Ready to Build Hardware-Integrated Web Applications?

Our team specializes in modern web development including WebUSB integration for hardware applications. Contact us to discuss your project.