tabmidi_

Route MIDI between
browser tabs.

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.

One message is all it takes to send a note.
window.postMessage(
  { type: "MIDI_OUT_TO_EXTENSION", midiData: [0x90, 60, 127] },
  "*"
)

Two reference instruments

Built with SvelteKit, with readable integration code on both sides (sending and receiving). Good starting points for your own instrument.

  • fmsynth.tabmidi.app

    Polyphonic FM synthesizer (Tone.js). Receives notes and synthesis parameters over MIDI: volume, pitch, harmonicity, envelope, reverb.

  • ambient.tabmidi.app

    Polyphonic Ambient synthetiser(Tone.js).

  • keyboard.tabmidi.app

    Playable keyboard and eight knobs assignable to any Control Change. Sends notes, CCs and pitch bend to the other tabs.

Install the extension

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).

Make your app compatible

Nothing to install. Everything goes through window.postMessage. Three things to know.

  1. 1 Send MIDI

    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)
  2. 2 Receive MIDI

    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
    });
  3. 3 Message format

    Bytes follow the MIDI standard [status, data1, data2]. The channel (0–15) is encoded in the low nibble of the status byte.

    MessageStatus byteData
    Note On0x90 – 0x9Fnote, velocity
    Note Off0x80 – 0x8Fnote, velocity
    Control Change0xB0 – 0xBFCC number, value
    Pitch Bend0xE0 – 0xEFLSB, MSB (14-bit)
    Clock0xF8—

Get Code on GitHub

You can download the extension and example instruments source code

An open little protocol for connecting web instruments. Made for developers and musicians.

tabmidi.app