MIDI Bridge works because the Web MIDI API exists and because the F1 protocol is, despite its quirks, fully documentable. This post walks the technical skeleton: how the browser talks to hardware, how the F1 encodes button presses and encoder turns into MIDI bytes, how we send RGB feedback back to the LEDs, and how profiles get stored and recalled from IndexedDB without losing a beat.
If you've wondered what happens inside midi.aidxn.com when you plug in your F1 and map a knob to a fader, this is the answer. No hand-waving. Just the bytes.
The Web MIDI API: Architecture at 30,000 Feet
The Web MIDI API is a standardized browser interface for sending and receiving MIDI data. It sits between your browser and the OS-level MIDI stack (CoreMIDI on macOS, WinMM on Windows, ALSA on Linux). When you call navigator.requestMIDIAccess(), the browser asks your OS, "What MIDI devices are plugged in?" and gives you a MIDIAccess object with lists of inputs and outputs.
Each input is a MIDIInput; each output is a MIDIOutput. They're not symmetrical—an input receives messages; an output sends them. Your F1, when connected via USB, shows up as both an input (the hardware sending you encoder turns and button presses) and an output (your code sending RGB LED bytes back). MIDI Bridge listens to the input, parses the events, and if a mapping is active, sends commands out the output to your DAW (or triggers a local LED update on the F1 itself).
The F1 Protocol: Hardware Meets Bytes
The F1 is a 16-pad grid with 4 buttons, 4 faders, and 4 rotary encoders. Every physical interaction gets encoded into MIDI.
Button Presses: Note On / Off
When you press a pad, the F1 sends a MIDI Note On message: channel, note number, velocity 127. When you release it, Note Off. The pad layout is fixed: bottom-left pad is note 54 (F#1 in MIDI pitch notation), bottom-right is 61 (C#2). Top-left is 70 (A#4), top-right is 77 (F5). Standard piano-roll mapping.
Each of the 4 buttons sends a different note. Button 1 = note 36 (C2). Button 4 = note 39 (D#2). Simple, predictable, no surprises. In MIDI Bridge, we map these incoming note messages to DAW commands using a profile JSON that says, "Note 54 on input channel 1 → trigger Ableton's Undo."
Faders and Encoders: CC (Control Change)
Faders send MIDI CC (Control Change) messages. The F1's 4 faders use CC 41–44. Each movement is a CC message with a value 0–127. Move the fader up, value goes high. Simple linear scaling. No relative mode, no weird acceleration curves—just raw position.
Encoders are trickier. They send CC messages too (CC 1–4 for the F1), but in relative mode. Turning the encoder clockwise sends CC +1. Counter-clockwise sends CC +127 (which the DAW interprets as –1 in 7-bit two's complement). This matters: a fader is absolute position, an encoder is relative change. In a DAW like Ableton, a fader might control pan (–∞ to +∞), while an encoder might control a parameter's fine nudge.
RGB LED Feedback: Sysex
Here's where it gets real. The F1 has RGB LEDs on every pad and button. To light them, you don't send Note On with a velocity trick—you send System Exclusive (Sysex) messages. Sysex is MIDI's escape hatch for manufacturer-specific commands. Native Instruments' device ID is 0x33.
A typical F1 LED sysex message looks like: 0xF0 0x00 0x21 0x33 0x02 0x01 0x02 <pad> <red> <green> <blue> 0xF7. That's: SysEx Start (0xF0), Native Instruments (0x00 0x21 0x33), F1 device type (0x02), command 0x01 (LED), pad index (0–15), then 8-bit RGB values (0–255 each), then SysEx End (0xF7).
If you want pad 0 (bottom-left) to be bright cyan, you send F0 00 21 33 02 01 02 00 00 FF FF F7. Red = 0, Green = 255, Blue = 255. The browser packs this byte array and sends it via midiOutput.send([...bytes...]). No latency. The LED responds in ~1ms.
MIDI Bridge uses sysex to update LED states in real-time. When you're in the mapping UI and hovering over a pad, we send a soft red glow to that pad on the hardware. When a mapping is active and you press a mapped button, we can flash green as feedback. It's live visual connection between screen and hardware.
Custom Mapping JSON Schema
A MIDI Bridge profile is a JSON document. It describes: which F1 (we support all of them, but you can tag it for your reference), which DAW, and a mapping table. Here's the shape:
{
"id": "f1-gen2-ableton-live-12",
"name": "F1 Gen 2 – Ableton Live 12",
"hardware": "f1_gen2",
"daw": "ableton_live",
"mappings": [
{
"source": {
"type": "note",
"note": 54,
"channel": 0,
"velocity_min": 0,
"velocity_max": 127
},
"target": {
"type": "cc",
"cc": 122,
"value": 127,
"channel": 0
},
"feedback": {
"type": "led",
"pad": 0,
"color_on": [0, 255, 0],
"color_off": [50, 50, 50]
}
}
],
"version": 1,
"created_at": "2026-06-11T14:22:00Z",
"updated_at": "2026-06-11T14:22:00Z"
}
Each mapping object says: when input event X happens, send output event Y, and optionally light up an LED with color Z. The source can be a note (pad/button), a CC (fader/encoder), or a relative CC (encoder only). The target can be a MIDI CC, a note, or a sysex message. The feedback object tells MIDI Bridge which LED to update and in what color.
When you export a profile, it's this JSON. When you import a profile, we deserialize it, validate the schema, and load it into IndexedDB. Every profile gets a UUID and a timestamp. If you update a profile, we keep the old version and create a new one, so you can roll back if a mapping breaks your workflow.
IndexedDB: Local Storage That Doesn't Suck
Profiles live in IndexedDB, not localStorage. localStorage is 5–10MB per origin; IndexedDB is gigabytes. We create an object store called profiles with indexes on hardware, daw, and created_at. When you open the app, we query all profiles, group by hardware, and populate the UI. Switching profiles means a single DB query and a re-render—instant.
Exporting a profile? We serialize it to JSON, wrap it in a blob, and trigger a download. Importing? We read the file, validate the schema against a Zod type, deserialize it, and insert it into the DB. If validation fails—malformed JSON, missing required fields—we throw a user-facing error. No silent failures.
IndexedDB also handles versioning. If MIDI Bridge introduces a new mapping schema in v2, we run a migration script that reads all v1 profiles, transforms them to v2, and re-inserts them. This keeps users' historical profiles intact without manual action.
Event Parsing: From Bytes to Actions
When you press the F1's bottom-left pad, midiInput.onmidimessage fires with a MIDIMessageEvent. The event has a data property: a Uint8Array of bytes. For a note-on event, it's [0x90, 0x36, 0x7F] (channel 1, note 54, velocity 127).
MIDI Bridge's parser extracts the status byte (0x90 tells us it's a Note On on channel 1), the note (0x36 = 54), and the velocity (0x7F = 127). We look up note 54 in the active profile's mappings. If there's a match—"note 54 → CC 122 value 127 on channel 0"—we construct the output bytes and send them to the DAW via midiOutput.send([0xB0, 0x7A, 0x7F]). CC 122, value 127, channel 0.
For encoders, the math is trickier. An encoder sending CC 1 with value 1 means "turn right one notch." Value 127 (bit-complement of –1) means "turn left one notch." Many DAWs have their own encoder acceleration, so we can optionally multiply the relative value—turn it 3 notches per hardware turn—to speed up parameter changes. This multiplier is part of the profile.
LED feedback runs on the same event loop. If the mapping has a feedback object, we extract the pad index and color, pack a sysex message, and send it to the F1's output. All in ~5ms from hardware press to LED glow. The browser's MIDI system queues messages if you're sending multiple at once, so rapid button presses don't cause collisions.
Real-Time vs. Latency: The Web MIDI Trade-off
Web MIDI's latency is 0–2ms on a quiet system, up to 5–10ms if the browser is under load. Compare that to Ableton's native mapping editor, which has ~0.1ms. In absolute terms, 2ms is perceivable (humans notice delays above 50ms), but in the context of music production and practice, it's invisible. You press a pad, the DAW responds. The delay is there, but your brain doesn't register it as lag.
For live performance—especially DJing or triggering samples in real-time—some players prefer native mapping. For studio work, sound design, and practice, the latency is a non-issue. MIDI Bridge trades imperceptible latency for the massive convenience of browser-based, cloud-syncable, shareable profiles.
Browser Compatibility in 2026
Chrome, Edge, and Opera have supported Web MIDI since 2015 (though Firefox only shipped it in 2023). Safari shipped it in macOS 14.6 and iOS 17. This means every modern browser can talk to MIDI hardware. No more native-app dependency. The spec is stable, ratified, and widely implemented.
The one caveat: Sysex messages require user permission. When you first open MIDI Bridge, the browser prompts you: "Allow MIDI Bridge to access your MIDI devices?" You click yes once. After that, MIDI Bridge can listen to inputs and send sysex without re-prompting. This is a security gate—prevents malicious scripts from silently listening to your keyboard's MIDI output, for example.
Why This Matters: Decoupling Hardware from Software
Before Web MIDI, your F1 lived in a walled garden. Native Instruments' software controlled it; your DAW's native mapping layer controlled it. You couldn't route it through a custom intermediary without buying third-party tools like Bome's MIDI Translator ($60, another software license).
Web MIDI + browser sandbox = you own the mapping layer. You write the logic. You store the profiles wherever you want (IndexedDB, cloud sync, email, GitHub). The hardware becomes genuinely universal. An F1 is just a dumb MIDI controller now; the intelligence lives in your mapping.
The Edge Cases MIDI Bridge Handles
Multiple F1s Plugged In
If you have two F1s, navigator.requestMIDIAccess() returns both as separate inputs and outputs. MIDI Bridge lets you select which F1 you're mapping. We store the device's name and product ID, so if you unplug and replug, we re-bind to the same device (assuming it gets the same device ID from the OS, which is usually true).
MIDI Channel Filtering
The F1 sends on channel 0 (sometimes reported as channel 1 in user-facing UIs, depending on whether you're 0-indexed or 1-indexed). If a mapping specifies "only respond to notes on channel 1," we check the status byte and skip messages on other channels. This lets you use one F1 as multiple virtual controllers in different DAWs if your setup supports multi-client MIDI routing.
Sysex Permissions and Fallback
Some browsers or user settings restrict sysex. If midiOptions.sysex is false in the MIDIAccess object, MIDI Bridge disables LED feedback and warns the user: "Sysex disabled—LED feedback won't work." The rest of the mapping (notes, CCs) continues to function. It's graceful degradation.
Frequently Asked Questions
Why not use a native Electron app instead of the browser?
Because the browser is better. It syncs, it's platform-agnostic, and you don't have to install anything. Electron would add 200MB to the download size and tie us to a single build pipeline. Web MIDI is stable and fast enough. The only reason to go native is if you need access to the OS layer directly (like reading MIDI timestamps with sub-millisecond precision), which MIDI Bridge doesn't.
Can you send sysex to a different MIDI device than the F1?
Yes. If you're mapping an Akai Maschine or a Launchpad, MIDI Bridge can send sysex to them too (we'd need to know their device IDs and sysex command structures, but the architecture supports it). Right now we're focused on the F1; adding other controllers is a matter of documenting their protocols and adding command templates to the app.
What if my DAW doesn't respond to MIDI CC 122?
That's a DAW limitation, not a MIDI Bridge limitation. Some DAWs have limited CC support (FL Studio has fewer mappable CCs than Reaper, for instance). MIDI Bridge lets you send any CC 0–119; whether the DAW listens is up to the DAW. We document the CC availability per DAW in the app's help section.
Does sysex work over MIDI over Bluetooth?
Yes, but with caveats. Bluetooth MIDI is lower-bandwidth than USB, so sysex messages are slower and more prone to drop-out if there's interference. For LED feedback over Bluetooth, expect 10–50ms latency instead of 1–2ms. For button/knob mapping, Bluetooth is fine. For realtime LED updates, USB is recommended.
Can I version-control my mappings?
Yes. Export your profile as JSON, commit it to GitHub, and you can track changes. We also support JSON-Patch for diffing profiles (showing exactly which mappings changed between versions). This is on the roadmap for v2.
The Future: Profile Community and Auto-Learn
The next phase of MIDI Bridge is a community profile library. Producers upload their F1–Ableton mapping, and you can download it, customize it, and save your own version. Think npm for MIDI profiles.
Beyond that, auto-learn mode: press a pad on your F1, then press a button in the DAW you want to map it to, and MIDI Bridge learns the connection. No manual CC lookup. Just physical action, captured.
The Bottom Line
The Web MIDI API is criminally underused. It's the bridge between hardware and software, and it's fast, standardized, and works everywhere. MIDI Bridge proves you can build a serious music tool entirely in the browser. No native app, no subscriptions, no lock-in. Just you, your F1, and a JSON file that travels with you forever.
If you're building music software or want to understand how MIDI works at the byte level, explore midi.aidxn.com, read the introductory post, and dig into the Web MIDI spec. It's a surprisingly elegant standard, and it's waiting for you to build something with it.