A DOM for MusicXML. Parse a score into a typed, queryable tree, edit it in musical terms, and serialize it back. Unknown tags round-trip verbatim.
bun add @stringsync/mdomimport { MDOMParser } from '@stringsync/mdom';
const doc = new MDOMParser().parseFromString(xml);
const doc2 = await new MDOMParser().parseFromBlob(blob); // compressed .mxl
const score = doc.score;import { MusicXMLSerializer, MXLSerializer } from '@stringsync/mdom';
new MusicXMLSerializer().serializeToString(doc); // string
await new MXLSerializer().serializeToBlob(doc); // .mxl Blobimport { MDocument } from '@stringsync/mdom';
const voice = MDocument.empty().score.addPart({ id: 'P1' }).addMeasure().getOrCreateVoice('1');
voice.addNote({ step: 'C', octave: 4, type: 'quarter' }); // append; mdom lays out the timing
voice.addChord(
[
{ step: 'E', octave: 4 },
{ step: 'G', octave: 4 },
],
{ type: 'quarter' }
);
const [note1, note2] = voice.notes;
note2.setPitch({ step: 'E', octave: 4, alter: -1 }); // retune, moves no time
note1.setDuration({ type: 'eighth' }); // reshape, ripples later notes in
note1.addTie(note2); // spanner
note2.convertToRest(); // silence, keep the beat
note2.remove(); // delete, onsets close the gapimport { MDocument, Cursor } from '@stringsync/mdom';
const voice = MDocument.empty().score.addPart({ id: 'P1' }).addMeasure().getOrCreateVoice('1');
voice.addNote({ step: 'C', octave: 4, type: 'quarter' });
voice.addNote({ step: 'D', octave: 4, type: 'quarter' });
const cursor = Cursor.at(voice); // immutable caret at (measure, voice, onset)
cursor.note; // C — the note under the caret
cursor.next()!.note; // D — movement returns a new caret; crosses barlines
cursor.next()!.next(); // null — past the last note (the append point)See e2e for worked examples.
bun link # install the mdom cli
mdom test # run the test suite
mdom fix # typecheck, format, lint