Back to Hub
HARDWARE INTEGRATION •

Talk to Real Hardware From a Web Page.

No plugins, no Electron, no native drivers to install: modern Chromium can open serial ports, pair with Bluetooth devices, read raw HID input, flash USB microcontrollers, and receive MIDI — all from JavaScript, all permission-gated. This guide covers each API with working patterns and the honest limits (because every one of these has sharp edges).

Universal rules first: these APIs require HTTPS (or localhost), a user gesture to request access, and — with few exceptions — they are Chromium-only. If your users are on Firefox or Safari, you need a fallback plan.

Web Serial: Arduino and IoT debugging

The most practical of the bunch. Talk to Arduinos, ESP32s, and industrial controllers over USB-serial straight from the page:

const port = await navigator.serial.requestPort(); // user picks the device
await port.open({ baudRate: 115200 });
const writer = port.writable.getWriter();
await writer.write(new TextEncoder().encode('LED_ON\n'));
writer.releaseLock();
// Reading: port.readable is a stream of Uint8Array chunks.
const reader = port.readable.getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}

Same API works for industrial hardware — PLCs, sensors, barcode scanners — anything speaking serial. Always releaseLock() your readers/writers and close() the port, or the device stays locked until the tab closes.

WebUSB: flashing microcontrollers

Serial covers UART devices; WebUSB goes lower — raw USB transfers for flashing firmware or talking to devices without a serial interface:

const device = await navigator.usb.requestDevice({
  filters: [{ vendorId: 0x2341 }] // e.g. Arduino's VID
});
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
// Now: device.transferOut(endpoint, firmwareChunk)

Honest caveat: the OS must not have already claimed the device with a kernel driver, and devices in bootloader/DFU mode sometimes need specific handling. This is genuinely how browser-based firmware flashers (like ESP Web Tools) work — it's production-proven, just fiddly.

WebHID: raw keyboards and custom controllers

WebHID exposes Human Interface Devices at the raw report level — great for custom macro pads, game controllers, and reading keyboard scancodes the OS would normally swallow:

const [device] = await navigator.hid.requestDevice({ filters: [] });
await device.open();
device.addEventListener('inputreport', (e) => {
  const { data, reportId } = e;
  handleRawInput(reportId, new Uint8Array(data.buffer));
});

Security note: for obvious reasons, the browser blocks access to protected usages like standard keyboards and mice via WebHID — you get niche/custom devices, not a keylogger API.

Web Bluetooth: BLE peripherals

Connect to Bluetooth Low Energy devices — heart-rate monitors, smart bulbs, sensor tags:

const device = await navigator.bluetooth.requestDevice({
  filters: [{ services: ['battery_service'] }]
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService('battery_service');
const char = await service.getCharacteristic('battery_level');
const value = await char.readValue();
console.log('Battery:', value.getUint8(0) + '%');

Web Bluetooth realities: GATT only — no classic Bluetooth audio. Range and reliability vary wildly by OS. And every connection starts with a browser picker the page can't bypass; there's no silent background scanning for websites.

Web MIDI: hardware knobs for your app

MIDI controllers make fantastic physical interfaces — map knobs to app parameters, pads to macros:

const access = await navigator.requestMIDIAccess();
for (const input of access.inputs.values()) {
  input.onmidimessage = (e) => {
    const [status, note, velocity] = e.data;
    if (status === 144 && velocity > 0) triggerMacro(note);
  };
}

Fun fact: this works for far more than music. A $30 MIDI pad becomes 16 physical macro buttons for any web app — streamers and video editors have used this trick for years.

WebNN: local AI, with eyes open

WebNN (navigator.ml) promises GPU/NPU-accelerated ML inference in the browser. The honest status: it's still emerging, with real implementations only in some Chromium builds and APIs that have changed between drafts. If you need local inference today, WASM-based runtimes (like ONNX Runtime Web or Transformers.js) are the proven path; keep an eye on WebNN for the future.

Prototype the dashboard in NitroIDE: every snippet above runs in a real browser preview. Build your device dashboard UI — charts, controls, log viewers — against mocked data first, then wire in the hardware APIs when you're on a machine with the actual device plugged in.

Build Hardware Dashboards in the Browser.

Prototype device UIs with zero setup — then connect real hardware.

Launch NitroIDE