A JavaScript library for generating expressive multi-track MIDI files.
- Multi-track MIDI file generation
- Written in TypeScript with full type definitions
- Works in Node.js (CJS & ESM) and the browser
- Note names (
C#4,Eb5) or raw MIDI numbers - Chords, arpeggios, grace notes, pitch bends, and more
- Experimental VexFlow integration
npm install midi-writer-jsimport MidiWriter from 'midi-writer-js';
const track = new MidiWriter.Track();
track.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'D4', 'E4'], duration: '4', sequential: true}));
const writer = new MidiWriter.Writer(track);
console.log(writer.dataUri());const MidiWriter = require('midi-writer-js');
const track = new MidiWriter.Track();
track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '2'}));
const writer = new MidiWriter.Writer(track);
console.log(writer.dataUri());import MidiWriter from 'midi-writer-js';
const track = new MidiWriter.Track();
track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '2'}));
const writer = new MidiWriter.Writer(track);
const data: Uint8Array = writer.buildFile();import MidiWriter from 'midi-writer-js';
const track = new MidiWriter.Track();
track.addEvent([
new MidiWriter.NoteEvent({pitch: ['E4', 'D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}),
new MidiWriter.NoteEvent({pitch: ['E4', 'D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}),
new MidiWriter.NoteEvent({pitch: ['C4', 'C4', 'C4', 'C4', 'D4', 'D4', 'D4', 'D4'], duration: '8'}),
new MidiWriter.NoteEvent({pitch: ['E4', 'D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}),
], function(event, index) {
return {sequential: true};
}
);
const writer = new MidiWriter.Writer(track);
console.log(writer.dataUri());When pitch is an array, the notes play as a chord by default. Set sequential: true to play them one after another instead.
// Chord (notes play simultaneously)
track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '1'}));
// Arpeggio (notes play sequentially)
track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '8', sequential: true}));Pass an array of tracks to Writer to create a multi-track MIDI file.
import MidiWriter from 'midi-writer-js';
const melody = new MidiWriter.Track();
melody.addTrackName('Melody');
melody.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
melody.addEvent(new MidiWriter.NoteEvent({pitch: ['E5', 'D5', 'C5'], duration: '4', sequential: true}));
const bass = new MidiWriter.Track();
bass.addTrackName('Bass');
bass.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 33}));
bass.addEvent(new MidiWriter.NoteEvent({pitch: ['C2'], duration: '1'}));
const writer = new MidiWriter.Writer([melody, bass]);
console.log(writer.dataUri());import MidiWriter from 'midi-writer-js';
const track = new MidiWriter.Track();
// Set volume via CC #7
track.addEvent(new MidiWriter.ControllerChangeEvent({controllerNumber: 7, controllerValue: 100}));
// Pitch bend ranging from -1.0 to 1.0 (0 = no bend)
track.addEvent(new MidiWriter.PitchBendEvent({bend: 0.5}));
track.addEvent(new MidiWriter.NoteEvent({pitch: ['E4'], duration: '2'}));
const writer = new MidiWriter.Writer(track);
console.log(writer.dataUri());| Method | Description |
|---|---|
addEvent(event, mapFunction?) |
Add one or more events. Supports method chaining. |
setTempo(bpm, tick?) |
Set tempo in beats per minute. |
setTimeSignature(numerator, denominator) |
Set time signature. |
setKeySignature(sf, mi?) |
Set key signature (e.g., 'C', 'Dm', 'F#'). |
setPitchBend(bend) |
Set pitch bend (-1.0 to 1.0). |
controllerChange(number, value, channel?, delta?) |
Add a controller change event. |
addTrackName(text) |
Set the track name. |
addText(text) |
Add a text event. |
addCopyright(text) |
Add a copyright notice. |
addInstrumentName(text) |
Set the instrument name. |
addMarker(text) |
Add a marker event. |
addCuePoint(text) |
Add a cue point event. |
addLyric(text) |
Add a lyric event. |
mergeTrack(track) |
Merge another track's events into this track. |
removeEventsByName(name) |
Remove all events of a given type. |
polyModeOn() |
Enable poly mode. |
| Name | Type | Default | Description |
|---|---|---|---|
| pitch | string or array | Each pitch can be a string or valid MIDI note code. Format for string is C#4. You can use the output from tonal to build scales, chords, etc. |
|
| duration | string or array | '4' |
How long the note should sound (see Duration Values). If an array of durations is passed then the sum will be used. |
| wait | string or array | 0 |
Rest before sounding note. Takes same values as duration. |
| sequential | boolean | false |
If true, array of pitches plays sequentially instead of as a chord. |
| velocity | number | 50 |
How loud the note should sound, values 1-100. |
| repeat | number | 1 |
How many times this event should repeat. |
| channel | number | 1 |
MIDI channel to use (1-16). |
| grace | string or array | Grace note(s) applied before the main note. Takes same format as pitch. |
|
| startTick | number | Explicit tick position for this event. If supplied, wait is ignored. |
|
| tick | number | Alias for startTick. |
| Method | Returns | Description |
|---|---|---|
buildFile() |
Uint8Array |
Build the MIDI file as a byte array. |
base64() |
string |
Base64-encoded string of the MIDI file. |
dataUri() |
string |
Data URI string (useful for playback or download links). |
stdout() |
— | Write MIDI file to stdout (Node.js CLI usage). |
setOption(key, value) |
Writer |
Set an option on the writer instance. |
| Class | Description |
|---|---|
NoteEvent |
High-level note event that generates NoteOn/NoteOff pairs. |
NoteOnEvent |
Raw MIDI note-on event. |
NoteOffEvent |
Raw MIDI note-off event. |
ProgramChangeEvent |
Change the instrument (patch) on a channel. |
PitchBendEvent |
Pitch bend event (-1.0 to 1.0). |
ControllerChangeEvent |
Controller change (CC) event. |
TempoEvent |
Set tempo (BPM). |
TimeSignatureEvent |
Set time signature. |
KeySignatureEvent |
Set key signature. |
TextEvent |
General text meta event. |
CopyrightEvent |
Copyright meta event. |
TrackNameEvent |
Track/sequence name meta event. |
InstrumentNameEvent |
Instrument name meta event. |
MarkerEvent |
Marker meta event. |
CuePointEvent |
Cue point meta event. |
LyricEvent |
Lyric meta event. |
EndTrackEvent |
End of track meta event (added automatically). |
See the full API documentation for details on each class.
| Value | Duration |
|---|---|
1 |
Whole |
2 |
Half |
d2 |
Dotted half |
dd2 |
Double dotted half |
4 |
Quarter |
4t |
Quarter triplet |
d4 |
Dotted quarter |
dd4 |
Double dotted quarter |
8 |
Eighth |
8t |
Eighth triplet |
d8 |
Dotted eighth |
dd8 |
Double dotted eighth |
16 |
Sixteenth |
16t |
Sixteenth triplet |
32 |
Thirty-second |
64 |
Sixty-fourth |
Tn |
Explicit number of ticks (e.g., T128 = 1 beat) |
MidiWriterJS can export MIDI from VexFlow voices, though this feature is experimental.
// ...VexFlow code defining notes
const voice = create_4_4_voice().addTickables(notes);
const vexWriter = new MidiWriter.VexFlow();
const track = vexWriter.trackFromVoice(voice);
const writer = new MidiWriter.Writer([track]);
console.log(writer.dataUri());- Example with Magenta player by Dirk Krause @dirkk0
MIT