JavaScript library for MIDI communication that simplifies sending and receiving MIDI messages between browsers/Node.js and MIDI instruments
—
The Utilities class provides static helper methods for MIDI data conversion, note processing, and value transformations. These utilities are essential for working with MIDI data in various formats and performing common conversions.
Convert between different note representations and formats.
class Utilities {
/**
* Convert note identifier to MIDI note number
* @param identifier - Note identifier (e.g., "C4", "F#3", "Bb5")
* @param octaveOffset - Octave offset to apply (default: 0)
* @returns MIDI note number (0-127)
*/
static toNoteNumber(identifier: string, octaveOffset?: number): number;
/**
* Convert MIDI note number to note identifier
* @param number - MIDI note number (0-127)
* @param octaveOffset - Octave offset to apply (default: 0)
* @returns Note identifier (e.g., "C4", "F#3")
*/
static toNoteIdentifier(number: number, octaveOffset?: number): string;
/**
* Get detailed note information from number or string
* @param value - Note input (string or number)
* @returns Object with note details (name, octave, number, etc.)
*/
static getNoteDetails(value: string | number): {
name: string;
octave: number;
number: number;
accidental?: string;
};
/**
* Guess MIDI note number from various input formats
* @param input - Note input (string, number, Note object)
* @param octaveOffset - Octave offset to apply
* @returns MIDI note number (0-127)
*/
static guessNoteNumber(input: any, octaveOffset?: number): number;
}Usage Examples:
import { Utilities } from "webmidi";
// Convert note names to numbers
console.log(Utilities.toNoteNumber("C4")); // 60
console.log(Utilities.toNoteNumber("A4")); // 69
console.log(Utilities.toNoteNumber("F#3")); // 54
console.log(Utilities.toNoteNumber("Bb5")); // 82
// With octave offset
console.log(Utilities.toNoteNumber("C4", 1)); // 72 (C5)
console.log(Utilities.toNoteNumber("C4", -1)); // 48 (C3)
// Convert numbers to note names
console.log(Utilities.toNoteIdentifier(60)); // "C4"
console.log(Utilities.toNoteIdentifier(69)); // "A4"
console.log(Utilities.toNoteIdentifier(54)); // "F#3"
// Get note details
const details = Utilities.getNoteDetails("F#4");
console.log(details.name); // "F#"
console.log(details.octave); // 4
console.log(details.number); // 66
console.log(details.accidental); // "#"
// Guess note number from various inputs
console.log(Utilities.guessNoteNumber("C4")); // 60
console.log(Utilities.guessNoteNumber(60)); // 60
console.log(Utilities.guessNoteNumber("middle C")); // Attempts to parseCreate Note objects and arrays from various inputs.
/**
* Build a Note object from input
* @param input - Note input (string, number, Note object)
* @param options - Note creation options
* @returns Note object
*/
static buildNote(input: any, options?: {
duration?: number;
attack?: number;
release?: number;
octaveOffset?: number;
}): Note;
/**
* Build array of Note objects from various inputs
* @param notes - Note inputs (single note or array)
* @param options - Note creation options
* @returns Array of Note objects
*/
static buildNoteArray(notes: any, options?: {
duration?: number;
attack?: number;
release?: number;
octaveOffset?: number;
}): Note[];Usage Examples:
// Build single notes
const note1 = Utilities.buildNote("C4");
const note2 = Utilities.buildNote(60, { attack: 0.8 });
const note3 = Utilities.buildNote(existingNote);
// Build note arrays
const chord = Utilities.buildNoteArray(["C4", "E4", "G4"]);
const octave = Utilities.buildNoteArray([60, 62, 64, 65, 67, 69, 71, 72]);
const withOptions = Utilities.buildNoteArray(["C4", "D4", "E4"], {
duration: 1000,
attack: 0.7
});Convert between different MIDI value formats.
/**
* Convert 7-bit MIDI value to float (0-1)
* @param value - 7-bit value (0-127)
* @returns Float value (0-1)
*/
static from7bitToFloat(value: number): number;
/**
* Convert float to 7-bit MIDI value
* @param value - Float value (0-1)
* @returns 7-bit value (0-127)
*/
static fromFloatTo7Bit(value: number): number;
/**
* Convert MSB/LSB pair to float value
* @param msb - Most significant byte (0-127)
* @param lsb - Least significant byte (0-127, default: 0)
* @returns Float value (0-1)
*/
static fromMsbLsbToFloat(msb: number, lsb?: number): number;
/**
* Convert float value to MSB/LSB pair
* @param value - Float value (0-1)
* @returns Object with msb and lsb properties
*/
static fromFloatToMsbLsb(value: number): {msb: number; lsb: number};Usage Examples:
// 7-bit conversions
console.log(Utilities.from7bitToFloat(0)); // 0
console.log(Utilities.from7bitToFloat(64)); // ~0.504
console.log(Utilities.from7bitToFloat(127)); // 1
console.log(Utilities.fromFloatTo7Bit(0)); // 0
console.log(Utilities.fromFloatTo7Bit(0.5)); // 63
console.log(Utilities.fromFloatTo7Bit(1)); // 127
// MSB/LSB conversions (for 14-bit values like pitch bend)
console.log(Utilities.fromMsbLsbToFloat(64, 0)); // Center position
console.log(Utilities.fromMsbLsbToFloat(127, 127)); // Maximum value
const msbLsb = Utilities.fromFloatToMsbLsb(0.75);
console.log(msbLsb.msb); // MSB value
console.log(msbLsb.lsb); // LSB valueApply offsets to MIDI note numbers with validation.
/**
* Apply octave and semitone offsets to a MIDI note number
* @param number - Original MIDI note number (0-127)
* @param octaveOffset - Octave offset (default: 0)
* @param semitoneOffset - Semitone offset (default: 0)
* @returns Modified MIDI note number (0-127)
*/
static offsetNumber(number: number, octaveOffset?: number, semitoneOffset?: number): number;Usage Examples:
// Apply offsets
console.log(Utilities.offsetNumber(60)); // 60 (no change)
console.log(Utilities.offsetNumber(60, 1)); // 72 (up one octave)
console.log(Utilities.offsetNumber(60, -1)); // 48 (down one octave)
console.log(Utilities.offsetNumber(60, 0, 7)); // 67 (up perfect fifth)
console.log(Utilities.offsetNumber(60, 1, -5)); // 67 (up octave, down fourth = fifth)
// Results are clamped to valid MIDI range (0-127)
console.log(Utilities.offsetNumber(120, 1)); // 127 (clamped to max)
console.log(Utilities.offsetNumber(5, -1)); // 0 (clamped to min)Sanitize and convert channel inputs to proper formats.
/**
* Sanitize channel input to array of valid channel numbers (1-16)
* @param channel - Channel input (number, string, or array)
* @returns Array of channel numbers (1-16)
*/
static sanitizeChannels(channel: number | string | number[] | "all"): number[];Usage Examples:
// Single channel
console.log(Utilities.sanitizeChannels(1)); // [1]
console.log(Utilities.sanitizeChannels("5")); // [5]
// Multiple channels
console.log(Utilities.sanitizeChannels([1, 3, 5])); // [1, 3, 5]
// All channels
console.log(Utilities.sanitizeChannels("all")); // [1, 2, 3, ..., 16]
console.log(Utilities.sanitizeChannels([])); // [1, 2, 3, ..., 16]
// Invalid channels are filtered out
console.log(Utilities.sanitizeChannels([0, 1, 17])); // [1] (0 and 17 removed)Convert time values to MIDI timestamps.
/**
* Convert time value to high-resolution timestamp
* @param time - Time value (number, string, or relative)
* @returns Timestamp in milliseconds
*/
static toTimestamp(time?: number | string): number;Usage Examples:
// Current time
console.log(Utilities.toTimestamp()); // Current timestamp
// Absolute time
console.log(Utilities.toTimestamp(1000)); // 1000ms from epoch
// Relative time (if supported)
console.log(Utilities.toTimestamp("+500")); // 500ms from now
// String parsing
console.log(Utilities.toTimestamp("1s")); // 1 second (if supported)Find properties and names by values.
/**
* Get property name by value in an object
* @param object - Object to search
* @param value - Value to find
* @returns Property name or undefined
*/
static getPropertyByValue(object: object, value: any): string | undefined;
/**
* Get control change name by CC number
* @param number - CC number (0-127)
* @returns Control change name
*/
static getCcNameByNumber(number: number): string;
/**
* Get control change number by name
* @param name - CC name
* @returns CC number (0-127) or undefined
*/
static getCcNumberByName(name: string): number | undefined;
/**
* Get channel mode name by number
* @param number - Channel mode number
* @returns Channel mode name
*/
static getChannelModeByNumber(number: number): string;Usage Examples:
// Property lookup
const ccMap = { volume: 7, pan: 10, sustain: 64 };
console.log(Utilities.getPropertyByValue(ccMap, 7)); // "volume"
// CC name lookup
console.log(Utilities.getCcNameByNumber(1)); // "modulation"
console.log(Utilities.getCcNameByNumber(7)); // "volume"
console.log(Utilities.getCcNameByNumber(64)); // "sustain"
// CC number lookup
console.log(Utilities.getCcNumberByName("volume")); // 7
console.log(Utilities.getCcNumberByName("sustain")); // 64
console.log(Utilities.getCcNumberByName("unknown")); // undefined
// Channel mode lookup
console.log(Utilities.getChannelModeByNumber(120)); // "allsoundoff"
console.log(Utilities.getChannelModeByNumber(123)); // "allnotesoff"Detect the runtime environment.
/**
* Whether running in Node.js environment
* @returns True if in Node.js
*/
static get isNode(): boolean;
/**
* Whether running in browser environment
* @returns True if in browser
*/
static get isBrowser(): boolean;Usage Examples:
// Environment detection
if (Utilities.isNode) {
console.log("Running in Node.js");
// Use Node.js specific functionality
}
if (Utilities.isBrowser) {
console.log("Running in browser");
// Use browser specific functionality
}
// Conditional imports/requires
const midiAccess = Utilities.isBrowser
? navigator.requestMIDIAccess()
: require('jzz')();function processNoteInput(input, options = {}) {
// Sanitize input to note number
const noteNumber = Utilities.guessNoteNumber(input);
// Apply offsets if specified
const offsetNote = options.octaveOffset || options.semitoneOffset
? Utilities.offsetNumber(noteNumber, options.octaveOffset, options.semitoneOffset)
: noteNumber;
// Convert back to identifier
const identifier = Utilities.toNoteIdentifier(offsetNote);
// Build final Note object
return Utilities.buildNote(identifier, options);
}
// Usage
const note1 = processNoteInput("C4", { octaveOffset: 1 });
const note2 = processNoteInput(60, { semitoneOffset: 7 });function scaleControllerValue(rawValue, targetMin = 0, targetMax = 1) {
// Convert to float first
const normalized = Utilities.from7bitToFloat(rawValue);
// Scale to target range
return targetMin + (normalized * (targetMax - targetMin));
}
function prepareForMidi(floatValue) {
// Clamp to valid range
const clamped = Math.max(0, Math.min(1, floatValue));
// Convert to MIDI format
return Utilities.fromFloatTo7Bit(clamped);
}
// Usage
const controllerValue = scaleControllerValue(100, 0, 127); // Scale CC to 0-127
const midiValue = prepareForMidi(0.75); // Convert 0.75 to MIDI valuefunction distributeToChannels(channels, callback) {
const validChannels = Utilities.sanitizeChannels(channels);
validChannels.forEach(channel => {
callback(channel);
});
}
// Usage
distributeToChannels([1, 3, 5], (channel) => {
output.channels[channel - 1].sendProgramChange(1);
});
distributeToChannels("all", (channel) => {
output.channels[channel - 1].sendControlChange("volume", 100);
});interface NoteDetails {
name: string;
octave: number;
number: number;
accidental?: string;
}
interface MsbLsbValue {
msb: number;
lsb: number;
}
type ChannelInput = number | string | number[] | "all";
type NoteInput = string | number | Note;
type TimeInput = number | string;Install with Tessl CLI
npx tessl i tessl/npm-webmidi