A postMessage event bus wires your web music apps — sequencers, synths,
controllers — together, with no drivers and no native software. The Web MIDI Tab Router extension handles the routing; your apps just speak the protocol below.
window.postMessage(
{ type: "MIDI_OUT_TO_EXTENSION", midiData: [0x90, 60, 127] },
"*"
)Built with SvelteKit, with readable integration code on both sides (sending and receiving). Good starting points for your own instrument.
Polyphonic FM synthesizer (Tone.js). Receives notes and synthesis parameters over MIDI: volume, pitch, harmonicity, envelope, reverb.
Polyphonic Ambient synthetiser(Tone.js).
Playable keyboard and eight knobs assignable to any Control Change. Sends notes, CCs and pitch bend to the other tabs.
Web MIDI Tab Router installs in one click, works offline and needs no configuration. It's what relays messages from one tab to another.
Also works on Chromium-based browsers (Edge, Brave, Opera).
Nothing to install. Everything goes through window.postMessage. Three things to
know.
Post an array of standard MIDI bytes. The extension relays it to every other open tab.
// Send MIDI to the other tabs
function sendToRouter(bytes) {
window.postMessage({
type: "MIDI_OUT_TO_EXTENSION",
midiData: Array.from(bytes)
}, "*");
}
sendToRouter([0x90, 60, 127]); // Note On — C4, velocity 127
sendToRouter([0xB0, 74, 100]); // Control Change — CC 74
sendToRouter([0xF8]); // MIDI Clock (tempo pulse)Listen for incoming messages and drive your instrument. The first (status) byte carries both the message type and the channel.
// Receive the MIDI relayed by the extension
window.addEventListener("message", (event) => {
const msg = event.data;
if (msg?.type !== "MIDI_IN_FROM_EXTENSION") return;
const [status, data1, data2] = msg.midiData;
const command = status & 0xF0; // message type
const channel = status & 0x0F; // channel 0–15
if (command === 0x90 && data2 > 0) playNote(data1, data2); // Note On
if (command === 0xB0) setParam(data1, data2); // CC
});Bytes follow the MIDI standard [status, data1, data2]. The channel (0–15) is
encoded in the low nibble of the status byte.
| Message | Status byte | Data |
|---|---|---|
| Note On | 0x90 – 0x9F | note, velocity |
| Note Off | 0x80 – 0x8F | note, velocity |
| Control Change | 0xB0 – 0xBF | CC number, value |
| Pitch Bend | 0xE0 – 0xEF | LSB, MSB (14-bit) |
| Clock | 0xF8 | — |
You can download the extension and example instruments source code