WebHID
Uncommon HID devices from a web page: special controllers, pedals, tablets, programmable keyboards…
HID is the protocol behind keyboards, mice and controllers. WebHID lets a page talk to devices the operating system does not fully handle: it reads their raw input reports and sends them output and feature reports (lights, rumble, configuration).
Desktop Chrome and Edge only, since 89. Chrome for Android has it as a developer trial in 154 and plans to ship it in 157. Mozilla and WebKit oppose it on security grounds.
What it is for
- Configuring peripherals without installing software
- Advanced controller features (lights, gyroscope)
- Pedals, button boxes and studio controllers
Demo
Connect a HID device and pick it in the chooser: a PlayStation or Switch controller, a drawing tablet, a pedal, a button box… You will see its raw input reports, byte by byte; changing bytes light up. Keyboards and mice are blocked for security.
Browser support
Limited availability| Feature | Desktop | Mobile | |||||
|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS | |
| navigator.hid | 89Chrome: 89 | 89Edge: 89 | —Firefox: No | —Safari: No | 154flag1Chrome: 154 flag | —Firefox: No | —Safari: No |
- 1Developer trial in 154; planned to ship in Chrome 157.
Compatibility generated on every deploy from MDN browser-compat-data 8.1.3 (24 Sep 2026) · web-features 3.40.0 · checked by hand: 1 Oct 2026
Minimal code
<!doctype html>
<meta charset="utf-8">
<title>WebHID · ejemplo mínimo</title>
<!-- Lee los informes de entrada en bruto de un dispositivo HID (mando, teclado especial, pedal…).
Chrome y Edge de escritorio. HTTPS o localhost. -->
<button id="connect">Conectar dispositivo HID</button>
<pre id="out"></pre>
<script>
const out = document.querySelector('#out');
const hex = (view) => [...new Uint8Array(view.buffer, view.byteOffset, view.byteLength)].map((b) => b.toString(16).padStart(2, '0')).join(' ');
document.querySelector('#connect').onclick = async () => {
if (!('hid' in navigator)) return (out.textContent = 'WebHID no está disponible en este navegador.');
// Sin filtros se listan todos; en producción filtra por vendorId/productId o por usagePage
const [device] = await navigator.hid.requestDevice({ filters: [] });
if (!device) return;
await device.open();
out.textContent = `${device.productName} · vendor 0x${device.vendorId.toString(16)} · product 0x${device.productId.toString(16)}\n`;
device.addEventListener('inputreport', (e) => {
out.textContent = `${device.productName}\ninforme ${e.reportId}: ${hex(e.data)}`;
});
};
// Los dispositivos ya autorizados vuelven sin preguntar
navigator.hid?.getDevices().then((devices) => devices.length && console.log('Autorizados:', devices));
</script><!doctype html>
<meta charset="utf-8">
<title>WebHID · ejemplo mínimo</title>
<!-- Lee los informes de entrada en bruto de un dispositivo HID (mando, teclado especial, pedal…).
Chrome y Edge de escritorio. HTTPS o localhost. -->
<button id="connect">Conectar dispositivo HID</button>
<pre id="out"></pre>
<script>
const out = document.querySelector('#out');
const hex = (view) => [...new Uint8Array(view.buffer, view.byteOffset, view.byteLength)].map((b) => b.toString(16).padStart(2, '0')).join(' ');
document.querySelector('#connect').onclick = async () => {
if (!('hid' in navigator)) return (out.textContent = 'WebHID no está disponible en este navegador.');
// Sin filtros se listan todos; en producción filtra por vendorId/productId o por usagePage
const [device] = await navigator.hid.requestDevice({ filters: [] });
if (!device) return;
await device.open();
out.textContent = `${device.productName} · vendor 0x${device.vendorId.toString(16)} · product 0x${device.productId.toString(16)}\n`;
device.addEventListener('inputreport', (e) => {
out.textContent = `${device.productName}\ninforme ${e.reportId}: ${hex(e.data)}`;
});
};
// Los dispositivos ya autorizados vuelven sin preguntar
navigator.hid?.getDevices().then((devices) => devices.length && console.log('Autorizados:', devices));
</script>What’s new
Keep in mind
- requestDevice() needs a user gesture; getDevices() returns already-authorised devices without asking.
- Keyboards, mice and other sensitive devices are blocked by design.
- Works in dedicated workers since Chrome 131 (but requestDevice() only in the window).
Further reading
Sources
- Chrome Status · release notes 15422 Sep 2026
- Blink · Intent to Prototype WebHID on Android13 Aug 2026
- WHATWG SG #264Aug 2026
- Mozilla standards-positions #459accessed 1 Oct 2026
- WebKit standards-positions #510accessed 1 Oct 2026