The Web MIDI API lets your browser talk to MIDI devices. This page gives you a bunch of copy pasteable snippets for these basic interactions, that I basically collected from one of the below links.
Spec | MDN | Polyfill (read the fine print re: its dependencies)
function connect() {
navigator.requestMIDIAccess()
.then(
(midi) => midiReady(midi),
(err) => console.log('Something went wrong', err));
}
function midiReady(midi) {
// Also react to device changes.
midi.addEventListener('statechange', (event) => initDevices(event.target));
initDevices(midi); // see the next section!
}
If you have any MIDI devices connected, they will appear here. You can also simulate them if you don't have any, but this depends on OS/what you have installed, so you need to figure it out.
Inputs:function initDevices(midi) {
// Reset.
midiIn = [];
midiOut = [];
// MIDI devices that send you data.
const inputs = midi.inputs.values();
for (let input = inputs.next(); input && !input.done; input = inputs.next()) {
midiIn.push(input.value);
}
// MIDI devices that you send data to.
const outputs = midi.outputs.values();
for (let output = outputs.next(); output && !output.done; output = outputs.next()) {
midiOut.push(output.value);
}
displayDevices();
startListening();
}
// Start listening to MIDI messages.
function startListening() {
for (const input of midiIn) {
input.addEventListener('midimessage', midiMessageReceived);
}
}
To test this, start mashing on the buttons/keys of a connected input device.
function midiMessageReceived(event) {
// MIDI commands we care about. See
// http://webaudio.github.io/web-midi-api/#a-simple-monophonic-sine-wave-midi-synthesizer.
const NOTE_ON = 9;
const NOTE_OFF = 8;
const cmd = event.data[0] >> 4;
const pitch = event.data[1];
const velocity = (event.data.length > 2) ? event.data[2] : 1;
// You can use the timestamp to figure out the duration of each note.
const timestamp = Date.now();
// Note that not all MIDI controllers send a separate NOTE_OFF command for every NOTE_ON.
if (cmd === NOTE_OFF || (cmd === NOTE_ON && velocity === 0)) {
console.log(`🎧 from ${event.srcElement.name} note off: pitch:${pitch}, velocity: ${velocity}`);
// Complete the note!
const note = notesOn.get(pitch);
if (note) {
console.log(`🎵 pitch:${pitch}, duration:${timestamp - note} ms.`);
notesOn.delete(pitch);
}
} else if (cmd === NOTE_ON) {
console.log(`🎧 from ${event.srcElement.name} note off: pitch:${pitch}, velocity: {velocity}`);
// One note can only be on at once.
notesOn.set(pitch, timestamp);
}
}
To test this, press one of these buttons and listen to the audio coming out of your output device.
Pitch:function sendMidiMessage(pitch, velocity, duration) {
const NOTE_ON = 0x90;
const NOTE_OFF = 0x80;
const device = midiOut[selectOut.selectedIndex];
const msgOn = [NOTE_ON, pitch, velocity];
const msgOff = [NOTE_ON, pitch, velocity];
// First send the note on;
device.send(msgOn);
// Then send the note off. You can send this separately if you want
// (i.e. when the button is released)
device.send(msgOff, Date.now() + duration);
}