BLE-MIDI over Web Bluetooth

airmidi

Connect to BLE-MIDI devices directly from the browser — no OS-level pairing, no native app. Talks the MIDI GATT service straight over Web Bluetooth.

01 · Connect

Pair with a BLE-MIDI device

Opens the browser's native Bluetooth device picker, scoped to BLE-MIDI peripherals, and connects straight to the MIDI I/O characteristic. Requires a device already in pairing/advertising mode.

Your browser doesn't support Web Bluetooth. Try Chrome, Edge, Opera, or Android — Safari and Firefox aren't supported.
Not connected
Connect
import { connectBleMidi } from "airmidi";

const connection = await connectBleMidi();
console.log("connected to", connection.deviceName);

// If your device doesn't show up in the picker, it may not advertise the
// MIDI service UUID. Widen the scan to list every nearby device instead:
await connectBleMidi({ wideScan: true });
Disconnect
connection.addEventListener("disconnected", () => {
  console.log("device disconnected");
});

connection.disconnect();
02 · Send & receive

Play the keyboard

The connection is bi-directional over the same characteristic: click or drag across a key to send a Note On / Note Off to your connected device, and notes played on the device (or by anything else routed to it) come back and light up the same keyboard.

You played (sent) Received
no notes held

Drag across the keys to slide between notes — like a real glissando.

Message log
Receiving
connection.addEventListener("midimessage", (event) => {
  const { data, timestamp } = event.detail;
  console.log(Array.from(data), timestamp);
});
Sending
// Note On — channel 1, middle C (60), velocity 100
await connection.send({ data: [0x90, 60, 100] });

// Note Off
await connection.send({ data: [0x80, 60, 0] });
03 · Low-level API

Parser & encoder playground

No device needed — this runs the same BleMidiParser and encodeBleMidiPackets that power the connection above, decoding raw BLE-MIDI packet bytes (header + timestamp framing, running status, SysEx) and encoding MIDI messages back into packets.

Decode: BLE-MIDI packet bytes → MIDI messages
import { BleMidiParser } from "airmidi";

const parser = new BleMidiParser();
const messages = parser.parsePacket(
  Uint8Array.of(0x80, 0x8d, 0x90, 0x3c, 0x40)
);
// [{ data: Uint8Array[0x90, 0x3c, 0x40], timestamp: 13 }]
Encode: MIDI message → BLE-MIDI packet bytes
import { encodeBleMidiPackets } from "airmidi";

const packets = encodeBleMidiPackets([
  { data: [0x90, 0x3c, 0x64] },
]);
// [Uint8Array[0x80, ts, 0x90, 0x3c, 0x64]]