Glossary

WebHID

WebHID is a browser API that lets a web page exchange reports with a Human Interface Device, such as a game controller, macro pad or custom button box. HID stands for Human Interface Device, the USB class used by keyboards and mice. WebHID is a Web Platform Incubator Community Group draft, not a W3C Recommendation, and it is exposed as navigator.hid in secure contexts only.

How it works

A page cannot enumerate every HID device. It calls navigator.hid.requestDevice() with a list of filters, and the browser shows a chooser. The call needs transient activation, such as a click. Without it the promise rejects with a SecurityError. The result is an array of HIDDevice objects, because one chosen item can represent several HID interfaces and the browser must then grant all of them.

Each filter can contain four fields: vendorId, productId, usagePage and usage. The spec also defines exclusionFilters, which remove devices that would otherwise match. Devices the user already approved come back from navigator.hid.getDevices() without a prompt, so a page can reconnect on the next visit.

Talking to a device follows a fixed sequence:

  • Call open() on the HIDDevice. It returns a promise.
  • Listen for the inputreport event. Its HIDInputReportEvent carries the reportId and a DataView of the bytes.
  • Send data with sendReport(reportId, data), or use sendFeatureReport() and receiveFeatureReport() for feature reports.
  • Call close() when finished, or forget() to drop the saved permission.

The browser parses the device report descriptor and exposes the result as a collections array, so you can read which usage pages and report IDs a device defines. A usage is a 32-bit value: the usage page sits in the high 16 bits and the usage ID in the low 16 bits. If the interface does not use report IDs, pass 0 as the reportId. If it does, 0 is reserved and must not be used.

const usage = (page, id) => ((page << 16) | id) >>> 0;
const hex = n => "0x" + n.toString(16).padStart(8, "0");
const filter = { vendorId: 0xabcd, usagePage: 0x000c, usage: 0x0001 };
console.log("filter vendorId:", filter.vendorId, "=", "0x" + filter.vendorId.toString(16));
console.log("consumer control usage:", hex(usage(0x0c, 0x01)));
console.log("keyboard usage:", hex(usage(0x01, 0x06)));
const sendReportId = (usesReportIds, id) => usesReportIds ? (id === 0 ? "reserved, do not use 0" : "report " + id) : "pass 0";
console.log(sendReportId(false, 0), "|", sendReportId(true, 0), "|", sendReportId(true, 2));

Output:

filter vendorId: 43981 = 0xabcd
consumer control usage: 0x000c0001
keyboard usage: 0x00010006
pass 0 | reserved, do not use 0 | report 2

Which browsers support WebHID?

WebHID works in Chrome and Edge 89 and later and in Opera 76 and later. MDN compatibility data lists no support in Firefox or Safari, and it flags the API as experimental. Feature-detect with "hid" in navigator before showing any connect button.

Does WebHID block keyboards and security keys?

Yes, the spec relies on a blocklist file kept in the WICG webhid repository to restrict which devices a site can reach. It also notes that a browser can deny keyboard-like devices by checking for a top-level collection that uses the keyboard usage. Do not build a feature that depends on reaching a standard keyboard.

Common pitfalls

  • Calling requestDevice() on page load: with no transient activation it rejects with SecurityError. Call it from a click handler.
  • Reading the result as one device: requestDevice() resolves to an array. Use devices[0] and handle an empty array when the user closes the chooser.
  • Using the wrong report ID: sending report ID 0 to a device that uses report IDs, or a nonzero ID to one that does not, fails. Read the collections to learn the IDs.
  • Forgetting open(): sendReport() and inputreport events do nothing until open() has resolved.
  • Sending a blocked report: if the blocklist covers a report, sendReport() rejects with NotAllowedError even though open() worked.
  • Blocking it in an iframe: the "hid" feature can be disabled by Permissions-Policy, which makes calls reject with SecurityError.

Related terms

  • WebUSB — sibling API for vendor-specific USB devices
  • Web Bluetooth — the sibling API for Bluetooth Low Energy devices
  • HTTPS — the secure context navigator.hid requires
  • Permissions-Policy header — controls the hid feature in embedded frames
  • DOM — the document model that dispatches the connection events

See also

  • Term: WebUSB — the closest alternative when the device has no HID interface