Blog Protocol 8 min read

WebMIDI in 2026 — Browser-Only Gamepad Rig

WebMIDI gamepad rigs in 2026 — Chrome, Firefox, Safari all ship it. Run a browser-only DualSense to MIDI bridge with no driver install.

By Aidxn Design

WebMIDI gamepad rigs were impossible to take seriously in 2017. The API existed in Chrome but no other browser implemented it, Safari refused to ship, and you couldn't open a sysex channel without a flag. In 2026, every major desktop browser ships WebMIDI by default. The Gamepad API has been stable since 2014. Put them together and you can drive a hardware synth, Ableton, or a WebGL VJ tool from a DualSense — with zero install. This post is the state of the art.

TL;DR
  • Browser support: Chrome 43+, Firefox 108+, Safari 16.4+, Edge 79+. ~96% of desktop traffic.
  • Permission: WebMIDI asks once per origin. Sysex needs an explicit opt-in.
  • Latency cost: ~1.5 ms over native on Chromium, ~3 ms on Safari. Live-usable.
  • Gamepad polling: requestAnimationFrame at 60 Hz (~16.7 ms). Diff-and-send to avoid CC spam.
  • The catch: backgrounded tabs throttle. Keep the tab visible during performance.

What WebMIDI actually exposes

The W3C WebMIDI spec exposes a single asynchronous entry point: navigator.requestMIDIAccess(). It resolves with a MIDIAccess object containing two maps — inputs for devices the browser can read from, and outputs for devices it can write to. Both maps fire statechange events when devices appear or disappear, so you can hot-plug a controller without reloading the page.

Sending a MIDI message is one line: output.send([0x90, 60, 100]) dispatches a Note On for C4 at velocity 100. There is an optional second argument for scheduled timestamps, which is the bit nobody mentions but matters enormously — output.send(data, performance.now() + 50) queues the message 50 ms in the future, giving you sample-accurate timing without busy-waiting in JavaScript.

// Minimal WebMIDI + Gamepad rig — no library, runs in any modern browser
const access = await navigator.requestMIDIAccess({ sysex: false });
const output = [...access.outputs.values()][0];   // first MIDI out

let lastState = [];
function tick() {
    const pads = navigator.getGamepads();
    const gp = pads[0];
    if (gp) {
        // Send CC 16 from left stick X, CC 17 from left stick Y
        const x = Math.round((gp.axes[0] + 1) * 63.5);   // -1..1 → 0..127
        const y = Math.round((gp.axes[1] + 1) * 63.5);
        if (x !== lastState[0]) output.send([0xB0, 16, x]);
        if (y !== lastState[1]) output.send([0xB0, 17, y]);
        lastState = [x, y];
    }
    requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

Browser support, broken down

WebMIDI was a Chromium-only party for years. The 2022–2025 stretch changed that. As of June 2026:

BrowserVersion landedSysexNotes
Chrome / Edge43 (Apr 2015)YesReference implementation. Most stable.
Opera30 (Jul 2015)YesSame engine as Chrome.
Firefox108 (Dec 2022)YesRequires Permissions-Policy header on iframes.
Safari (macOS, iPadOS)16.4 (Mar 2023)YesAdds ~1.5 ms latency vs Chrome.
Safari (iPhone)flaggedYesBehind Experimental Features as of mid-2026.
Brave / Vivaldi / ArcInherit ChromiumYesNo additional flags.

The permission prompt

WebMIDI is gated by a one-time prompt per origin. Without sysex, the prompt reads "Allow this site to control MIDI devices?" — once approved, the permission persists indefinitely. With sysex, the prompt explicitly warns about firmware writes, which is correct because sysex can flash device firmware. The bridge's browser playground only requests sysex when you tick the sysex checkbox, so casual visitors never see the scary prompt.

Gamepad API + WebMIDI — the loop

The W3C Gamepad API is poll-based — there is no "axis changed" event. You call navigator.getGamepads() once per frame, diff against the previous frame, and emit MIDI messages for changes. Polling rate matters:

  • requestAnimationFrame: 60 Hz on most displays, 120 Hz on ProMotion/144Hz panels. Default choice.
  • setInterval(fn, 4): 250 Hz polling. Smoother but burns CPU and won\'t outrun the gamepad\'s own internal rate.
  • Web Worker + OffscreenCanvas: keeps polling alive when the tab is backgrounded. Hack of last resort.

Gotchas you will hit

Six things that bit us building the playground:

  1. Backgrounded tabs throttle to 1 Hz. Switch tabs mid-performance and your gamepad input freezes. Keep the tab visible.
  2. The first gamepad button press is required. Chromium hides connected gamepads until any input arrives — fire a button on launch.
  3. Safari uses a different standard layout. Triggers on Safari are axes[6] and axes[7]; on Chrome they're buttons[6] and buttons[7]. Detect and branch.
  4. Output buffers when the tab loses focus. Queued messages flush when the tab returns, which can produce a stuck-note burst. Flush before blur.
  5. Sysex over WebMIDI is chunked to ~256 bytes. Larger dumps must be split. See sysex preset transfer for the chunking pattern.
  6. HTTPS is required. WebMIDI refuses to load on http://. localhost is exempt.

Latency — browser vs native

We benchmarked the same gamepad-to-Ableton path through three transports: native Universal Controller MIDI desktop app, WebMIDI in Chrome 126, and WebMIDI in Safari 17.5. Each test sent 1,000 CC messages and timed the round trip to Ableton's monitor.

TransportMedian (ms)p95 (ms)Notes
Native desktop bridge1.42.1Reference baseline.
WebMIDI in Chrome 1262.94.4+1.5 ms vs native.
WebMIDI in Firefox 1273.14.8Marginally slower than Chrome.
WebMIDI in Safari 17.54.46.8+3 ms — still live-usable.

When the browser rig actually wins

Native always wins on raw latency. The browser rig wins everywhere else:

  • Zero install. Open the URL, hit a button, you\'re live. Massive for workshops, classrooms, and demos.
  • Sharable presets. URL-encode the mapping and you can DM a friend a link that loads their exact rig.
  • Embedded VJ tools. WebGL visualisers can subscribe to your CC stream and react in the same page, no IPC needed.
  • Education. Inspecting output.send in DevTools is a faster way to teach MIDI than reading the spec.

Try the browser playground or read about the cross-protocol comparison in OSC vs MIDI. For the desktop install path, head back to Universal Controller MIDI.

Keep reading

More setup walkthroughs