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.
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.
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 });
connection.addEventListener("disconnected", () => {
console.log("device disconnected");
});
connection.disconnect();
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.
Drag across the keys to slide between notes — like a real glissando.
connection.addEventListener("midimessage", (event) => {
const { data, timestamp } = event.detail;
console.log(Array.from(data), timestamp);
});
// 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] });
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.
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 }]
import { encodeBleMidiPackets } from "airmidi";
const packets = encodeBleMidiPackets([
{ data: [0x90, 0x3c, 0x64] },
]);
// [Uint8Array[0x80, ts, 0x90, 0x3c, 0x64]]