CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-webmidi

JavaScript library for MIDI communication that simplifies sending and receiving MIDI messages between browsers/Node.js and MIDI instruments

Pending
Overview
Eval results
Files

midi-output.mddocs/

MIDI Output

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.

Capabilities

Output Port Management

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;
}

Core Message Sending

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();

System Real-Time Messages

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;

System Common Messages

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;

Channel Messages (All Channels)

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;

Registered Parameter Numbers (RPN)

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;

OutputChannel

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;
}

Channel-Specific Note Control

/**
 * 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);

Channel-Specific Control Messages

/**
 * 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;

Advanced Channel Parameters

/**
 * 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;

Channel Mode Messages

/**
 * 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;

Usage Examples

Basic Output Usage

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 10

Advanced Timing and Scheduling

const 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
    });
  });
});

Types

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

docs

constants.md

index.md

message-forwarding.md

message-processing.md

midi-input.md

midi-output.md

note-processing.md

utilities.md

webmidi-interface.md

tile.json