MIDI (Musical Instrument Digital Interface) is a protocol for sending musical control data between devices — not audio itself, but instructions like "play middle C", "release that note", or "turn this knob to 75." Your keyboard, drum pad, or fader box sends these tiny messages; your code decides what they mean musically.
The browser exposes MIDI through the low-level Web MIDI API. This tutorial uses
WEBMIDI.js v3,
which wraps that API with friendly methods like addListener(),
playNote(), and sendControlChange() so you can focus on making sound
instead of decoding binary messages.
Everything runs in your web browser — nothing to install. Copy these lines into your HTML file. Load them in this order: p5.js, p5.sound, ProControls, then WEBMIDI.js.
<!-- Google icon font (optional — used by ProControls) -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=block" rel="stylesheet">
<!-- p5.js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.13/lib/p5.min.js"></script>
<!-- p5.sound (requires p5.js above) -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.13/lib/addons/p5.sound.min.js"></script>
<!-- ProControls -->
<script src="https://cdn.jsdelivr.net/gh/dstein-art/proControls4p5js@v0.8.3/ProControls.js"></script>
<!-- WEBMIDI.js v3 (IIFE build) -->
<script src="https://cdn.jsdelivr.net/npm/webmidi@3.1.14/dist/iife/webmidi.iife.min.js"></script>
<!-- Your sketch -->
<script src="sketch.js"></script>
https://,
localhost, or file:// pages. Use Chrome, Edge, or Opera, plug in a
MIDI controller (or virtual MIDI port), and allow access when the browser prompts you.
Call WebMidi.enable() after a user click — the same gesture that unlocks audio
with userStartAudio(). In v3, enable() returns a promise.
Once enabled, WebMidi.inputs lists every connected input port (your keyboard,
pad controller, etc.).
Click the preview to enable MIDI. Device names are printed to the
openConsolePanel() — a ProControls panel that captures
console.log() output.
If you have multiple MIDI devices, grab one by name or index instead of listening on all of them:
var input = WebMidi.getInputByName("IAC Driver Bus 1");
// or
var input = WebMidi.inputs[0];
noteon starts a note; noteoff stops it.
Use midiToFreq() to turn the MIDI note number into a pitch.
Velocity is how hard you press (0–127). In your noteon listener, map it to volume:
osc.amp(e.rawVelocity / 127, 0.01);
Knobs and faders send controlchange messages.
CC 1 is usually the mod wheel. Use e.rawValue (0–127) to drive a parameter.
To listen for one CC only, check e.controller.number inside the listener.
Keys usually send on channel 1; drum pads often send on channel 10.
Use input.channels[10] to listen only on that channel.
Add the { channels: [1] } option to a listener to target one channel.
Keys on channel 1 play a synth; pads on channel 10 trigger a sample.
var input = WebMidi.inputs[0];
input.addListener('noteon', function(e) {
osc.freq(midiToFreq(e.note.number));
osc.amp(0.5);
}, { channels: [1] });
input.addListener('noteon', function() {
clap.play();
}, { channels: [10] });
input.addListener('controlchange', function(e) {
filter.freq(map(e.rawValue, 0, 127, 300, 6000));
});
WEBMIDI.js can also send MIDI — useful if you want your sketch to drive
external hardware. Check out WebMidi.outputs,
output.playNote(), and output.sendControlChange() in the
WEBMIDI.js basics guide.
Combine MIDI input with the Effects and
Samples tutorials to build a full performance instrument.