JavaScript library for MIDI communication that simplifies sending and receiving MIDI messages between browsers/Node.js and MIDI instruments
—
MIDI Output functionality provides comprehensive capabilities for sending MIDI messages to instruments and devices. Output objects represent physical or virtual MIDI output ports, while OutputChannel objects provide channel-specific message sending.
Access and manage MIDI output ports.
class Output extends EventEmitter {
/**
* Array of 16 OutputChannel objects (channels 1-16)
*/
readonly channels: OutputChannel[];
/**
* Output port name as reported by the system
*/
readonly name: string;
/**
* Unique identifier for this output port
*/
readonly id: string;
/**
* Connection state: "closed", "open", or "pending"
*/
readonly connection: string;
/**
* Port state: "connected" or "disconnected"
*/
readonly state: string;
/**
* Device manufacturer name (if available)
*/
readonly manufacturer: string;
/**
* Port type (always "output")
*/
readonly type: string;
/**
* Octave offset for this output (-10 to 10)
*/
readonly octaveOffset: number;
}Send raw MIDI messages and system exclusive data.
/**
* Send raw MIDI message
* @param message - MIDI message as array or Uint8Array
* @param options - Send options
* @param options.time - When to send (0 = now, or timestamp)
* @param legacy - Legacy timestamp parameter (deprecated)
* @returns Output instance for chaining
*/
send(message: number[] | Uint8Array, options?: {time?: number}, legacy?: number): Output;
/**
* Send system exclusive message
* @param identification - Manufacturer ID bytes
* @param data - SysEx data bytes
* @param options - Send options
* @returns Output instance for chaining
*/
sendSysex(identification: number[], data?: number[], options?: {time?: number}): Output;
/**
* Clear all pending messages and stop all notes
* @returns Output instance for chaining
*/
clear(): Output;Usage Examples:
import { WebMidi } from "webmidi";
await WebMidi.enable({ sysex: true });
const output = WebMidi.outputs[0];
// Send raw MIDI message (note on, channel 1, C4, velocity 100)
output.send([0x90, 60, 100]);
// Send with timing
output.send([0x90, 60, 100], { time: WebMidi.time + 1000 });
// Send SysEx message
output.sendSysex([0x41], [0x10, 0x16, 0x12]); // Roland message
// Clear all messages
output.clear();Send MIDI system real-time messages for synchronization.
/**
* Send MIDI clock pulse
* @param options - Send options
* @returns Output instance for chaining
*/
sendClock(options?: {time?: number}): Output;
/**
* Send start message
* @param options - Send options
* @returns Output instance for chaining
*/
sendStart(options?: {time?: number}): Output;
/**
* Send continue message
* @param options - Send options
* @returns Output instance for chaining
*/
sendContinue(options?: {time?: number}): Output;
/**
* Send stop message
* @param options - Send options
* @returns Output instance for chaining
*/
sendStop(options?: {time?: number}): Output;
/**
* Send active sensing message
* @param options - Send options
* @returns Output instance for chaining
*/
sendActiveSensing(options?: {time?: number}): Output;
/**
* Send system reset message
* @param options - Send options
* @returns Output instance for chaining
*/
sendReset(options?: {time?: number}): Output;Send system common messages for timing and tuning.
/**
* Send timecode quarter frame
* @param value - Quarter frame value (0-127)
* @param options - Send options
* @returns Output instance for chaining
*/
sendTimecodeQuarterFrame(value: number, options?: {time?: number}): Output;
/**
* Send song position pointer
* @param value - Song position in beats (0-16383)
* @param options - Send options
* @returns Output instance for chaining
*/
sendSongPosition(value?: number, options?: {time?: number}): Output;
/**
* Send song select
* @param value - Song number (0-127)
* @param options - Send options
* @returns Output instance for chaining
*/
sendSongSelect(value?: number, options?: {time?: number}): Output;
/**
* Send tune request
* @param options - Send options
* @returns Output instance for chaining
*/
sendTuneRequest(options?: {time?: number}): Output;Send channel messages to all channels or specific channels.
/**
* Send control change message
* @param controller - Controller number (0-127) or name
* @param value - Controller value (0-1 or 0-127)
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @param legacy - Legacy options (deprecated)
* @returns Output instance for chaining
*/
sendControlChange(
controller: number | string,
value: number,
options?: {channels?: number | number[] | "all"; time?: number},
legacy?: object
): Output;
/**
* Send pitch bend message
* @param value - Pitch bend value (-1 to 1 or 0-16383)
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @param legacy - Legacy options (deprecated)
* @returns Output instance for chaining
*/
sendPitchBend(
value?: number,
options?: {channels?: number | number[] | "all"; time?: number},
legacy?: object
): Output;
/**
* Send program change message
* @param program - Program number (0-127 or 1-128)
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @param legacy - Legacy options (deprecated)
* @returns Output instance for chaining
*/
sendProgramChange(
program?: number,
options?: {channels?: number | number[] | "all"; time?: number},
legacy?: object
): Output;
/**
* Send channel aftertouch message
* @param pressure - Pressure value (0-1 or 0-127)
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @param legacy - Legacy options (deprecated)
* @returns Output instance for chaining
*/
sendChannelAftertouch(
pressure: number,
options?: {channels?: number | number[] | "all"; time?: number},
legacy?: object
): Output;
/**
* Send key aftertouch message
* @param note - Note to apply pressure to
* @param pressure - Pressure value (0-1 or 0-127)
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @returns Output instance for chaining
*/
sendKeyAftertouch(
note: string | number | Note,
pressure: number,
options?: {channels?: number | number[] | "all"; time?: number}
): Output;Send and manage RPN messages for extended control.
/**
* Send RPN value
* @param parameter - RPN parameter name or number
* @param data - Parameter data
* @param options - Send options
* @param options.channels - Target channels (default: "all")
* @param options.time - When to send
* @returns Output instance for chaining
*/
sendRpnValue(
parameter: string | number,
data: number | number[],
options?: {channels?: number | number[] | "all"; time?: number}
): Output;
/**
* Set registered parameter
* @param parameter - RPN parameter name or number
* @param data - Parameter data
* @param channel - Target channels (default: "all")
* @param options - Send options
* @returns Output instance for chaining
*/
setRegisteredParameter(
parameter: string | number,
data?: number[],
channel?: number | number[] | "all",
options?: {time?: number}
): Output;
/**
* Send pitch bend range RPN
* @param semitones - Semitone range (0-127)
* @param cents - Cent range (0-127)
* @param options - Send options
* @returns Output instance for chaining
*/
sendPitchBendRange(
semitones?: number,
cents?: number,
options?: {channels?: number | number[] | "all"; time?: number}
): Output;
/**
* Set pitch bend range
* @param semitones - Semitone range (0-127)
* @param cents - Cent range (0-127)
* @param channel - Target channels (default: "all")
* @param options - Send options
* @returns Output instance for chaining
*/
setPitchBendRange(
semitones?: number,
cents?: number,
channel?: number | number[] | "all",
options?: {time?: number}
): Output;Channel-specific output for targeted MIDI communication.
class OutputChannel extends EventEmitter {
/**
* Channel number (1-16)
*/
readonly number: number;
/**
* Parent Output object
*/
readonly output: Output;
/**
* Octave offset for this channel
*/
readonly octaveOffset: number;
}/**
* Play note(s) on this channel
* @param note - Note(s) to play
* @param options - Play options
* @param options.velocity - Note velocity (0-1, default: 0.5)
* @param options.duration - Note duration in ms
* @param options.time - When to play
* @param options.release - Release velocity (0-1)
* @returns OutputChannel instance for chaining
*/
playNote(note: string | number | Note | Array<string | number | Note>, options?: {
velocity?: number;
duration?: number;
time?: number;
release?: number;
}): OutputChannel;
/**
* Send note on message
* @param note - Note(s) to turn on
* @param options - Note on options
* @param options.velocity - Note velocity (0-1, default: 0.5)
* @param options.time - When to send
* @returns OutputChannel instance for chaining
*/
sendNoteOn(note: string | number | Note | Array<string | number | Note>, options?: {
velocity?: number;
time?: number;
}): OutputChannel;
/**
* Send note off message
* @param note - Note(s) to turn off
* @param options - Note off options
* @param options.velocity - Release velocity (0-1, default: 0.5)
* @param options.time - When to send
* @returns OutputChannel instance for chaining
*/
sendNoteOff(note: string | number | Note | Array<string | number | Note>, options?: {
velocity?: number;
time?: number;
}): OutputChannel;
/**
* Stop note(s) (alias for sendNoteOff)
* @param note - Note(s) to stop
* @param options - Stop options
* @returns OutputChannel instance for chaining
*/
stopNote(note: string | number | Note | Array<string | number | Note>, options?: {
velocity?: number;
time?: number;
}): OutputChannel;Usage Examples:
const output = WebMidi.outputs[0];
const channel1 = output.channels[0]; // Channel 1
// Play a single note
channel1.playNote("C4");
// Play with options
channel1.playNote("C4", {
velocity: 0.8,
duration: 2000, // 2 seconds
time: WebMidi.time + 500 // Play in 500ms
});
// Play multiple notes (chord)
channel1.playNote(["C4", "E4", "G4"], { velocity: 0.7 });
// Send note on/off manually
channel1.sendNoteOn("C4", { velocity: 0.8 });
setTimeout(() => {
channel1.sendNoteOff("C4");
}, 1000);/**
* Send control change on this channel
* @param controller - Controller number (0-127) or name
* @param value - Controller value (0-1 or 0-127)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendControlChange(controller: number | string, value: number, options?: {time?: number}): OutputChannel;
/**
* Send pitch bend on this channel
* @param value - Pitch bend value (-1 to 1 or 0-16383)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendPitchBend(value?: number, options?: {time?: number}): OutputChannel;
/**
* Send program change on this channel
* @param program - Program number (0-127 or 1-128)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendProgramChange(program: number, options?: {time?: number}): OutputChannel;
/**
* Send channel aftertouch on this channel
* @param pressure - Pressure value (0-1 or 0-127)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendChannelAftertouch(pressure: number, options?: {time?: number}): OutputChannel;
/**
* Send key aftertouch on this channel
* @param target - Note to apply pressure to
* @param pressure - Pressure value (0-1 or 0-127)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendKeyAftertouch(target: string | number | Note, pressure: number, options?: {time?: number}): OutputChannel;/**
* Send RPN value on this channel
* @param rpn - RPN parameter name or number
* @param data - Parameter data
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendRpnValue(rpn: string | number, data: number | number[], options?: {time?: number}): OutputChannel;
/**
* Send NRPN value on this channel
* @param nrpn - NRPN parameter number
* @param data - Parameter data
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendNrpnValue(nrpn: number, data: number | number[], options?: {time?: number}): OutputChannel;
/**
* Send pitch bend range on this channel
* @param semitones - Semitone range
* @param cents - Cent range
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendPitchBendRange(semitones: number, cents: number, options?: {time?: number}): OutputChannel;
/**
* Send master tuning on this channel
* @param value - Tuning value in cents
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendMasterTuning(value: number, options?: {time?: number}): OutputChannel;
/**
* Send modulation range on this channel
* @param semitones - Semitone range
* @param cents - Cent range
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendModulationRange(semitones: number, cents: number, options?: {time?: number}): OutputChannel;/**
* Send channel mode message
* @param command - Mode command name or number
* @param value - Command value (default: 0)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendChannelMode(command: string | number, value?: number, options?: {time?: number}): OutputChannel;
/**
* Send omni mode message
* @param state - Omni state (true/false or "on"/"off")
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendOmniMode(state: boolean | "on" | "off", options?: {time?: number}): OutputChannel;
/**
* Send all notes off message
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendAllNotesOff(options?: {time?: number}): OutputChannel;
/**
* Send all sound off message (emergency stop)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendAllSoundOff(options?: {time?: number}): OutputChannel;
/**
* Send local control message
* @param state - Local control state (true/false or "on"/"off")
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendLocalControl(state: boolean | "on" | "off", options?: {time?: number}): OutputChannel;
/**
* Send mono mode message
* @param channels - Number of channels for mono mode (1-16, default: 1)
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendMonoMode(channels?: number, options?: {time?: number}): OutputChannel;
/**
* Send reset all controllers message
* @param options - Send options
* @returns OutputChannel instance for chaining
*/
sendResetAllControllers(options?: {time?: number}): OutputChannel;
/**
* Destroy the output channel (cleanup)
*/
destroy(): void;import { WebMidi } from "webmidi";
await WebMidi.enable();
const output = WebMidi.getOutputByName("My Synthesizer");
// Play a simple melody
const melody = ["C4", "D4", "E4", "F4", "G4"];
melody.forEach((note, index) => {
output.channels[0].playNote(note, {
time: WebMidi.time + (index * 500),
duration: 400
});
});
// Send control changes
output.sendControlChange("volume", 100, { channels: [1, 2, 3] });
output.sendControlChange(64, 127); // Sustain pedal on all channels
// Change programs
output.sendProgramChange(1, { channels: 1 }); // Piano on channel 1
output.sendProgramChange(25, { channels: 10 }); // Steel guitar on channel 10const startTime = WebMidi.time + 1000; // Start in 1 second
// Schedule a chord progression
const chords = [
["C4", "E4", "G4"], // C major
["F4", "A4", "C5"], // F major
["G4", "B4", "D5"], // G major
["C4", "E4", "G4"] // C major
];
chords.forEach((chord, index) => {
chord.forEach(note => {
output.channels[0].playNote(note, {
time: startTime + (index * 2000), // 2 seconds per chord
duration: 1800,
velocity: 0.7
});
});
});interface SendOptions {
time?: number;
}
interface NoteOptions {
velocity?: number;
duration?: number;
time?: number;
release?: number;
}
interface ChannelOptions {
channels?: number | number[] | "all";
time?: number;
}Install with Tessl CLI
npx tessl i tessl/npm-webmidi