WebHID API: Connect JavaScript to Custom Hardware

The WebHID (Human Interface Device) API provides a mechanism for web applications to access specialized, vendor-specific, and unconventional hardware peripherals directly from the browser. This guide breaks down what the WebHID API is, explains why it is essential for interacting with devices not supported by standard browser APIs, and demonstrates how JavaScript establishes communication, handles input reports, and writes data back to connected hardware.

What is the WebHID API?

Human Interface Devices (HIDs) are hardware components that take input from or provide output to humans. While common devices like standard keyboards and mice are handled automatically by the operating system, many specialized peripherals—such as gamepads with unique inputs, flight simulator controllers, LED panels, medical equipment, and programmable macro decks—rely on custom or complex HID data formats.

The WebHID API allows web applications to communicate directly with these specialized devices. Instead of relying on proprietary desktop drivers or native software, developers can build web-based interfaces that discover, configure, read, and write raw HID reports to supported hardware.

Why WebHID is Necessary

Standard web APIs (such as the Gamepad API or standard Keyboard/Pointer events) abstract away device-specific capabilities to provide uniform behavior across all hardware. However, this abstraction strips away vendor-specific functionality, such as:

WebHID provides direct access to the device’s raw byte streams (Input Reports, Output Reports, and Feature Reports), giving developers full control over non-standard hardware features.

How JavaScript Interacts with Hardware

JavaScript communicates with HID devices using an asynchronous, promise-based lifecycle consisting of requesting permission, opening a connection, listening for incoming data, and sending outgoing commands.

1. Requesting Device Access

Browsers require an explicit user gesture (like a button click) to request device permissions. Developers specify filters using vendor IDs (vendorId) and product IDs (productId) to restrict the device picker to compatible hardware.

document.getElementById('connect-button').addEventListener('click', async () => {
  try {
    // Request a specific device or pass an empty array to show all compatible devices
    const [device] = await navigator.hid.requestDevice({
      filters: [{ vendorId: 0x1234, productId: 0x5678 }]
    });

    if (!device) return;

    await device.open();
    console.log(`Connected to: ${device.productName}`);
  } catch (error) {
    console.error('Connection failed:', error);
  }
});

2. Reading Input Reports

When a user interacts with the physical hardware (e.g., pressing a button or turning a knob), the device sends an Input Report. JavaScript captures these events using an inputreport event listener:

device.addEventListener('inputreport', (event) => {
  const { data, device, reportId } = event;

  // Read data bytes using a DataView
  const firstByte = data.getUint8(0);
  const secondByte = data.getUint8(1);

  console.log(`Report ID ${reportId} data:`, firstByte, secondByte);
});

3. Sending Data (Output and Feature Reports)

To control hardware features, such as changing an LED color or activating a motor, the web app sends Output Reports or Feature Reports using sendReport or sendFeatureReport:

async function setDeviceLed(device, red, green, blue) {
  // Output report format depends on the specific device specification
  const reportId = 0x01;
  const payload = new Uint8Array([red, green, blue]);

  await device.sendReport(reportId, payload);
}

Security and Privacy Controls

Because raw hardware access carries potential security risks, WebHID implements strict security boundaries: