Skip to main content

Basics

Enabling the Library

The first step to get started is to enable the library. To do that, you simply call WebMidi.enable(). Starting with v3, the enable() method returns a promise which is resolved when the library has been enabled:

WebMidi
.enable()
.then(() => console.log("WebMidi enabled!"))
.catch(err => alert(err));
caution

If you intend to use MIDI system exclusive messages, you must explicitly enable them by setting the sysex option to true:

WebMidi
.enable({sysex: true})
.then(() => console.log("WebMidi with sysex enabled!"))
.catch(err => alert(err));

Listing Available Devices

To interact with devices you need to know which Input and Output ports are available. Connect a MIDI device and try the following:

WebMidi
.enable()
.then(onEnabled)
.catch(err => alert(err));

function onEnabled() {

// Inputs
WebMidi.inputs.forEach(input => console.log(input.manufacturer, input.name));

// Outputs
WebMidi.outputs.forEach(output => console.log(output.manufacturer, output.name));

}

You should see your hardware and software devices appear in the console. Note that many devices make available several input and/or output ports.

You can retrieve a reference to an Input by using the getInputByName() or getInputById() methods:

const myInput = WebMidi.getInputByName("MPK mini 3");

Once you have a reference to the input, you can add listeners that will react when a message (such as a note press) arrives.

Listening For Incoming MIDI Messages

On a MIDI device, an input has 16 discrete channels. If you want to listen on all of them, you can add a listener directly on the Input object:

const myInput = WebMidi.getInputByName("MPK mini 3");
myInput.addListener("noteon", e => {
console.log(e.note.identifier);
})

Try playing a note on your device. You should see the note's name and octave in the console.

Obviously, you can listen to many more messages coming from your device. For a full list, check out the Input.addListener() documentation.

It is also possible to listen to messages coming from a specific MIDI channel. For example, when I press the drum pads on my Akai MPK Mini, the messages are sent to channel 10:

const myInput = WebMidi.getInputByName("MPK mini 3");
const mySynth = myInput.channels[10]; // <-- the MIDI channel (10)

mySynth.addListener("noteon", e => {
console.log(e.note.identifier, e.message.channel);
})

In this case, the listener only listens to noteon messages coming in from channel 10 of the input device.

Sending Outgoing MIDI Messages

To send messages to an external device, you must first get a reference to it. For that, you can use methods such as getOutputByName() or getOutputById():

const myOutput = WebMidi.getOutputByName("SP-404MKII");

Then, you can use various methods to send your message. For example, if you want to tell you sampler to turn all sounds off, you could do the following:

const myOutput = WebMidi.getOutputByName("SP-404MKII");
myOutput.sendAllSoundOff();

You can learn about all the the methods available to send data by looking at the documentation for the Output object.

You can send messages to a specific MIDI channel by first grabbing a reference to the channel you want. For example, on the Roland SP-404 MK II sampler, you can control a vocoder effet by sending a pitchbend message on channel 11:

const myOutput = WebMidi.getOutputByName("SP-404MKII");
const vocoder = myOutput.channels[11];
vocoder.sendPitchBend(-0.5);

In this case, the vocoder constant contains an OutputChannel object.

Code Examples

Here are various other examples to give you an idea of what is possible with the library. All the examples below only work if the library has first been properly enabled with WebMidi.enable().

Retrieve an output port/device using its id, name or array index

Different ways to retrieve an output. Beware that IDs are different from one platform to another and on Node.js the id is the same as the name.

let output1 = WebMidi.getOutputById("123456789");
let output2 = WebMidi.getOutputByName("Axiom Pro 25 Ext Out");
let output3 = WebMidi.outputs[0];

Play a note on a specific MIDI channel (1)

The channels property of an Output object contains references to 16 OutputChannel objects (1-16).

let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3");

Play a note on multiple channels at once

You can call playNote() (and various other methods) directly on the Output object. This allows you to play a note on several channels at once. For example, to play a note on channels 1, 2 and 3:

let output = WebMidi.outputs[0];
output.playNote("Gb4", [1, 2, 3]);

You can also create a Note object and pass it to the playNote() method:

const note = new Note("A4");
const output = WebMidi.outputs[0];
output.playNote(note);

Play a note on a specific MIDI channel

To play a note on a specific MIDI channel, you can use the playNote() method of the OutputChannel object (instead of the one on the Output object).

For example, to play a chord on MIDI channel 1:

let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote(["C3", "D#3", "G3"]);

Control note velocity

You can control attack and release velocities when playing a note by using the options parameter.

let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {attack: 0.5});

If you prefer to use raw (7 bit) values between 0 and 127, you can use the rawAttack option instead:

let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {rawAttack: 123});

Specify note duration

If you specify a duration (in decimal milliseconds) for the note, it will automatically be stopped after the duration has expired. For example, to stop it after 1 second (1000 ms):

let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3", {duration: 1000});

Schedule notes

You can specify an absolute or relative time to schedule note playback in the future:

WebMidi.outputs[0].channels[1].playNote("C3", {time: WebMidi.time + 3000});
WebMidi.outputs[0].channels[1].playNote("C3", {time: "+2000"});

You can retrieve the current time with WebMidi.time. The time is in milliseconds (decimal) relative to the navigation start of the document.

Manually stopping playback

You can stop playback of a note right away or in the future.

WebMidi.outputs[0].channels[1].stopNote("C3");
WebMidi.outputs[0].channels[1].stopNote("C3", {time: "+2500"});

Sending a control change (a.k.a. CC) message

There are various ways to send a control change message. The most common way is to send the message to a single channel. The first parameter is the controller, the second number is the value:

// Use controller number
WebMidi.outputs[0].channels[1].sendControlChange(72, 64);

// Use controller name
WebMidi.outputs[0].channels[1].sendControlChange("volumecoarse", 123);

As you can see above, you can use either a name or number (0-127) to identify the controller to target. A list of controller names can be found in the API reference.

You can also send the control change message to several channels at once by using the sendControlChange() method of the Output object:

// Send to channels 1 through 3
WebMidi.outputs[0].sendControlChange("pancoarse", 123, {channels: [1, 2, 3]});

// Send to all channels
WebMidi.outputs[0].sendControlChange(72, 56);

Set polyphonic aftertouch

Send polyphonic aftertouch message to channel 8:

WebMidi.outputs[0].channels[8].sendKeyAftertouch("B#3", 0.25);

Set pitch bend value

The value is between -1 and 1 (a value of 0 means no bend).

WebMidi.outputs[0].channels[8].sendPitchBend(-0.25);

You can set the range of the bend with OutputChannel.sendPitchBendRange().

Use Chained Methods

Most methods return this so you can chain them:

WebMidi.outputs[0].channels[8]
.sendPitchBend(-0.25)
.playNote("F4");

Listen to event on single channel

WebMidi.inputs[0].channels[1].addListener("noteon", e => {
console.log(`Received 'noteon' message (${e.note.name}${e.note.octave}).`);
});

Listen to event on multiple channels at once

If you add a listener to the Input instead of the InputChannel, you can listen on multiple channels at once by using the channels option:

WebMidi.inputs[0].addListener("controlchange", e => {
console.log(`Received 'controlchange' message.`, e);
}, {channels: [1, 2, 3]});

If you do not specify a channels option, the listener will listen on all channels.

Check for the presence of an event listener

let channel = WebMidi.inputs[0].channels[1];
let test = e => console.log(e);
channel.addListener('programchange', test);
console.log("Has event listener: ", channel.hasListener('programchange', test));

Remove listeners

let channel = WebMidi.inputs[0].channels[1];
let test = e => console.log(e);
channel.removeListener("noteoff", test); // specifically this one
channel.removeListener("noteoff"); // all noteoff on this channel
channel.removeListener(); // all listeners

What's Next

I hope this short guide helped you getting started. Obviously, the library can do a whole lot more. Some of that is covered in the Going Further section but all of it is detailed in the API documentation.

If you need help, you can ask questions in the Forum. If you want to stay posted, I suggest you subscribe to our low-volume newsletter and follow our @webmidijs account on Twitter.

Finally, if this software proves useful I cannot encourage you enough to support it by becoming a sponsor on GitHub.

-- Jean-Philippe