Opus

A MusicXML engraving engine written in Rust.

What it is

Opus parses MusicXML, lays the music out into pages, and renders the result to SVG, PDF, or - as above - a canvas via WebAssembly in the browser.

Why this exists

Score rendering today tends to be scattered: a web renderer, a desktop app, a PDF exporter and a mobile app usually end up as separate codebases with separate engraving logic, each with its own bugs and its own ceiling on quality. Opus is one engraving pipeline, written once in Rust, that every platform draws from, so improving the engraving benefits all of them at once instead of one at a time.

Built on that shared pipeline, output can be reshaped without touching the underlying music: larger notation for someone with limited vision, a softer, lower-contrast palette for someone sensitive to glare - as presentation settings layered over one accurate rendering, rather than a fixed image. A baked PDF page cannot offer that; the pane on the render page is this idea made concrete, not a demo of unrelated settings.

What is actually happening on this page

Every render on this site - including the pane on the render page - goes through the same pipeline:

  1. MusicXML text, parsed and validated.
  2. Engraved into pages by the layout engine.
  3. Flattened into a tagged Float32Array of geometry - not SVG, not DOM.
  4. ~200 lines of JavaScript decode that stream into Canvas2D calls.

Six record tags carry the whole stream: TAG_LINE, TAG_RECT, TAG_TEXT, TAG_POLYGON, TAG_CIRCLE, TAG_GLYPH. Text is drawn with the Bravura music font, force-loaded through the Font Loading API before the first render, because canvas text rasterizes synchronously and never repaints once a font arrives late.

Status

Work in progress. Beaming across measures and systems, tuplets, dynamics, lyrics, articulations, slurs and repeats are incomplete or absent. See the roadmap for what is known, planned, or intentionally left unfinished.

Using it

Build the wasm bindings, then drop the component on a page that keeps its runtime files together:

rustup target add wasm32-unknown-unknown
./scripts/build-wasm.sh
<script type="module" src="/path/to/web/music-xml.js"></script>
<music-xml file="score.musicxml"></music-xml>