Developer Overview
Viritura's notation renderer is available to other applications. You can show MNX music notation in a web page, a documentation site, a VS Code webview or your own editor without running the Viritura app.
Choose a layer
The renderer is split into three layers. Pick the highest one that fits, and drop down a layer only when you need more control.
| Package | Use it when | You provide |
|---|---|---|
@viritura/score-viewer-react |
You are building a React 19.2+ app and want components and hooks | Props |
@viritura/score-viewer |
You want a scrollable, zoomable score in any page or framework | A container element and the MNX document |
@viritura/score-engine |
You draw the score yourself: a custom canvas, overlays, export | Canvas, paper, zoom, scrolling and interaction |
The React package wraps the viewer and adds an optional control bar.
The viewer adds page presentation on top of the engine: paper, page arrangements, zoom, scrolling, virtualised canvases, loading and error states, and a playhead that can follow playback.
The engine lays out the music in WebAssembly and draws ink only: staves, notes, symbols and text. It never paints paper, page shadows, backgrounds or controls.
- Score Viewer React:
<ScoreViewer>,<ScoreView>anduseScoreEngine. - Score Viewer: embed a score with one call.
- Score Engine: layout, painting, geometry, SVG export and the playback timeline.
Try it first
- The MNX Playground is a live
<ScoreViewer>from Score Viewer React inhorizonview mode. Edit MNX on the left and the score re-engraves as you type. It's the quickest way to see how the renderer handles a document before you embed it. - The example library shows rendered MNX documents
covering the specification, Viritura extensions and engraving behaviour.
The MNX documentation examples used in the playground are also available as
plain
.mnxfiles, for example/mnx-samples/beams.mnx, which you can use as test input while you build your integration.
Get the packages
npm
npm install @viritura/score-viewer-react # React components, includes the two below
npm install @viritura/score-viewer # framework-free viewer, includes the engine
npm install @viritura/score-engine # engine only
Each package is versioned and released on its own, so the engine can ship
fixes without a viewer release. Each viewer depends on a caret range (^) of
the engine version it was tested with. Every engine change on main is also
published as a prerelease under the next tag
(npm install @viritura/score-engine@next); pin an exact prerelease if you
need a fix before the next stable release. They are ES modules
with TypeScript declarations, and have no dependencies beyond React (a peer
dependency of the React package) and lucide-react for its control icons.
@viritura/score-engine also contains the files the engine loads at runtime:
dist/wasm/, dist/fonts/ and dist/score-engine.worker.js. See
Serving the files for how to serve them from a bundled
app.
Release archive
Each engine release (tag score-engine-v<version>) has a
GitHub Release with a zip and
its SHA-256 checksum. The archive contains:
| File | Purpose |
|---|---|
score-engine.js |
The engine as a single ES module |
score-engine.worker.js |
Module worker for off-main-thread layout |
score-viewer.js |
The framework-free viewer; it imports ./score-engine.js |
*.d.ts, types/ |
TypeScript declarations |
wasm/, fonts/ |
The engine binary, plus the Bravura and Libertinus Serif fonts |
manifest.json |
Package and engine versions, source commit, file sizes and hashes |
examples/ |
A runnable page showing both the viewer and the engine |
Copy the unzipped directory into your static assets, or vendor it into your repository, and import the modules by URL:
import { mountScore } from "/vendor/score-engine/score-viewer.js";
The archive suits pages without a build step and hosts that vendor their dependencies. It doesn't include the React components, which are on npm.
Preview builds
Every push to main that changes the engine or the packages it bundles
uploads the same directory as a workflow artifact named
score-engine-<version>-<commit>, kept for 90 days. Use these to try an
unreleased fix, and pin a release for anything you ship.
Inside the Viritura repository
Viritura's own apps depend on the workspace packages directly and build the
WebAssembly from the same commit with pnpm wasm:build. They don't use the
npm packages or the release archive, so an engine change and the app change that relies on it
always ship together.
Serving the files
Keep the layout. The engine loads
wasm/,fonts/andscore-engine.worker.jsfrom one base URL. By default that is the directory of the engine module itself, which works when the files are served as they are published, as with the release archive.Pass
assetBaseUrlwhen you use a bundler. Vite, webpack and similar tools move the engine's code into their own output, away from its runtime files. Copy the three entries fromnode_modules/@viritura/score-engine/dist/into your static assets as part of your build, and pass their URL:mkdir -p public/score-engine cp -r node_modules/@viritura/score-engine/dist/{wasm,fonts,score-engine.worker.js} public/score-engine/<ScoreViewer mnx={mnx} assetBaseUrl="/score-engine/" />loadEngine()andmountScore()accept the same option. Copy the files again whenever you upgrade the package, so they match the engine code.Rewritten URLs. If your host rewrites asset URLs, as VS Code webviews do, pass the rewritten base URL as
assetBaseUrl.Use HTTP. ES modules and
fetchdon't work fromfile://. Serve the directory with any static file server.Allow WebAssembly in your Content Security Policy. Compiling the engine needs
script-src 'wasm-unsafe-eval'. Off-main-thread layout also needs the worker script to be allowed, andfont-srcmust allow thefonts/directory.Bravura is required. The engine always loads and registers the Bravura music font under the family name
Bravura. The layout is measured with Bravura's metrics, so other music fonts are not supported and can't be substituted.The text font is replaceable. Text such as titles, lyrics and expressions uses Libertinus Serif, registered under the family name
Viritura Serifso your page's ownseriffont is untouched. PasstextFont: falseto skip it. Text then uses your page'sseriffont, or aViritura Serifface you register yourself. The engine estimates text widths from typical serif proportions rather than one font's metrics, so any ordinary serif face lays out acceptably.
Stable part identifiers
Parts are identified by the id of the MNX part in the document. When a part
has no id, the engine uses a positional fallback, #0, #1 and so on,
counted from zero in document order. The same identifier appears in score
information, geometry, hit-testing and the playback timeline, so you can join
them without matching part names.
Versioning
engine.version reports the WebAssembly engine version, the package version and
the source commit. The archive's manifest.json records the same values and
the SHA-256 hash of every file. npm releases are published from GitHub Actions
with provenance, so
npm shows the commit and workflow that built each version. Each package's
CHANGELOG.md lists breaking changes.
Licensing
The code is MIT licensed. Bravura and Libertinus Serif are distributed under the
SIL Open Font License. The archive and the @viritura/score-engine package
include LICENSE, THIRD_PARTY_NOTICES.md and LICENSES/OFL-1.1.txt.