Render sheet music directly inside Typst using SMuFL-aware glyph placement and CeTZ drawing primitives.
Features
- Pure Typst: no WASM plugin and no LilyPond or MuseScore CLI dependency.
- Notes, rests, spacers, chords, accidentals, key signatures, time signatures, and supported clefs.
- Inline annotations: dynamics, hairpins, articulations, fingerings, chord symbols, expression text, staff text, staff markers, and lyrics.
- Notation features: beams, ties, slurs, tuplets, octave lines, trills, grace notes / acciaccaturas, repeat barlines, endings, and dotted notes.
- Inline clef changes, inline time-signature changes, manual spacing via repeated spaces, and explicit system breaks.
- Single-staff, grand-staff, and bracketed multi-staff layout with vertical beat alignment.
- Alternate SMuFL font support via
music-fontandmusic-font-metadata. - Crisp vector PDF output.
Quick Start
#import "@preview/scorify:0.2.0": score, melody
#melody(
title: "Scale",
key: "C",
time: "4/4",
music: "c4 d e f | g a b c'",
)
Compile with Bravura available to Typst:
typst compile your-file.typ --font-path /path/to/bravura/
Font Setup
Scorify defaults to Bravura plus bundled Bravura metadata.
- Download
Bravura.otffrom the Bravura releases page. - Either install it system-wide, or keep it in a project folder and pass that folder with
--font-path.
Alternate SMuFL Fonts
You can switch fonts with:
music-font: Typst font family namemusic-font-metadata: optional SMuFL metadata dictionary, usually loaded withjson(...)
#import "@preview/scorify:0.2.0": melody
#melody(
music: "c4 d e f | g a b c'",
music-font: "Your SMuFL Font",
music-font-metadata: json("your-smufl-metadata.json"),
)
API Reference
score()
Primary entry point for one or more staves.
#score(
staves: (
(clef: "treble", music: "c4 d e f | g a b c'"),
(clef: "bass", music: "c2 g | c1"),
),
key: "C",
time: "4/4",
title: "My Piece",
composer: "Composer Name",
staff-group: "grand",
)
| Parameter | Type | Default | Description |
|---|---|---|---|
staves |
array | () |
Array of staff dictionaries |
key |
string | "C" |
Key signature like "C", "G", "Bb", "f#" |
time |
string | none |
Time signature like "4/4", "6/8", "common", "cut" |
title |
string | none |
Piece title |
subtitle |
string | none |
Subtitle |
composer |
string | none |
Composer name |
arranger |
string | none |
Arranger name |
lyricist |
string | none |
Lyricist name |
staff-group |
string | "none" |
"none", "grand", or "bracket" |
staff-size |
length | 1.75mm |
Staff space distance |
system-spacing |
length | 12mm |
Vertical space between systems |
staff-spacing |
length | 8mm |
Vertical space between staves in a system |
lyric-line-spacing |
length | none |
Override stacked lyric line spacing |
music-font |
string | "Bravura" |
SMuFL font family |
music-font-metadata |
dictionary/none | none |
Optional metadata dictionary |
width |
length/auto | auto |
Explicit width or auto |
measures-per-line |
int | none |
Force a fixed number of measures per system |
Staff dictionaries support:
| Field | Type | Default | Description |
|---|---|---|---|
clef |
string | none |
Any supported clef, including octave-clef variants and "percussion" |
music |
string | "" |
Music string |
fingering-position |
string | "above" |
Default fingering position: "above" or "below" |
melody()
Single-staff convenience wrapper around score().
#melody(
music: "c4 d e f | g a b c'",
key: "C",
time: "4/4",
clef: "treble",
title: "My Melody",
composer: "Composer",
)
| Parameter | Type | Default | Description |
|---|---|---|---|
music |
string | "" |
Music string |
key |
string | "C" |
Key signature |
time |
string | none |
Time signature |
clef |
string | none |
Clef |
title |
string | none |
Title |
composer |
string | none |
Composer |
staff-size |
length | 1.75mm |
Staff space |
system-spacing |
length | 12mm |
Vertical space between systems |
lyric-line-spacing |
length | none |
Override stacked lyric line spacing |
music-font |
string | "Bravura" |
SMuFL font family |
music-font-metadata |
dictionary/none | none |
Optional metadata dictionary |
width |
length/auto | auto |
Width |
measures-per-line |
int | none |
Force a fixed number of measures per system |
Full Example
#import "@preview/scorify:0.2.0": score
#set page(margin: 1.5cm)
#score(
title: "Ode to Joy",
composer: "L. van Beethoven",
key: "D",
time: "4/4",
staff-group: "grand",
staves: (
(
clef: "treble",
music: "
f#4n[3][D] f# g a | a8[D/A] b g4 f#[A] e |
d[D] d e f# | f#4.[A] e8 e2 |
f#4[D] f# g a | a[A] g f# e |
d[D] d e f# | e4.[A] d8 d2[D] |
",
),
(
clef: "bass",
fingering-position: "below",
music: "
d1n[1] | a, | d | a, |
d | a, | d | a,2 d4 r |
",
),
),
)
Examples
Useful starting points in examples/:
ode-to-joy.typ: grand staff with chord symbols, fingerings, and dynamicstechniques.typ: dense mixed-notation showcaseinline-clef-changes.typ: mid-system clef changesgrace-notes.typ: grace notes and acciaccaturaslyrics-demo.typ: lyrics and multi-line lyric layoutclef-variants.typandalto-tenor-demo.typ: alternate clefsthree-endings.typ: repeat endings / voltas
Supported Clefs
Scorify supports:
"treble","bass","alto","tenor""treble-8a","treble-8b","treble-15a","treble-15b""bass-8a","bass-8b","bass-15a","bass-15b""percussion"
Example:
#score(
staves: (
(clef: "alto", music: "c4 d e f"),
(clef: "tenor", music: "g,4 a b c"),
),
)
Time Signatures
Examples of accepted inputs:
| Input | Meaning |
|---|---|
"4/4" |
Four quarter notes per measure |
"3/4" |
Three quarter notes per measure |
"6/8" |
Compound duple |
"2/2" |
Alla breve |
"common" or "C" |
Common time symbol |
"cut" or `"C |
"` |
#melody(music: "c4 d e f", time: "common")
#melody(music: "c2 d", time: "cut")
Music String Cheat Sheet
-
Notes and rhythm:
c4,d8.,f#4,g'2,a,16- Accidentals:
#,##,&,&&,= - Octave markers:
'raises,,lowers - Duration is sticky:
c4 d e f
- Accidentals:
-
Rests, spacers, and manual spacing:
r4,r8.,s4- Repeated spaces add extra horizontal gap:
c e g c
- Repeated spaces add extra horizontal gap:
-
Chords:
<c e g>4 -
Articulations:
>accent,*staccato,-tenuto,_fermata -
Ties and slurs:
c4~ c4,c4( d e) f -
Inline attachments
- Dynamics:
v[pp],v[mf],v[ff] - Staff text above:
text[Solo] - Expression text below:
exp[dolce] - Fingerings:
n[3],n_[2],n[1 3 5] - Chord symbols:
[C],[Am7],[D/F#] - Staff markers:
bm(breath mark),//(caesura),ds,coda
- Dynamics:
-
Spans and ornaments
- Hairpins:
cresc{c e g c},decresc{c' b a g} - Trills:
c4tr,tr{d'4 e' f' g'} - Grace notes:
grace{c16 d e} f4 - Acciaccatura-style slash:
grace{f#16 g a/} b4 - Octave lines:
8a{...},8b{...},15a{...},15b{...} - Tuplets:
{2,3:d4 e d} - Endings / voltas:
end{1.: f d e c | g g c c}
- Hairpins:
-
Structure
- Barlines:
|,||,|.,|:,||:,:|,:||,:|:,:||: - Forced beaming:
[and]where they are not parsed as chord symbols - Inline clef changes:
... bass ... treble ...(cue-sized mid-system) - Inline time-signature changes:
... 3/4 ... 5/4 ... common ... cut ... - Literal newlines force a system break
- Barlines:
-
Lyrics
- Attach with
l[...]:c4l[text] - Hyphen continuation:
l[text-] - Melisma/extender:
l[text_] - Carry the previous lyric state with plain
l - Stack multiple lyric lines by attaching multiple lyric entries to one event
- Attach with
Short example:
#score(
staff-group: "grand",
staves: (
(clef: "treble", music: "c4[Am]n[1] dtext[Solo] e4tr f | cresc{g a b c'} | end{1.: d'4 e' f' g'}"),
(clef: "bass", music: "c,4 e, g, c bass b, a, g, | grace{c16 d e/} f4 | 3/4 c e g"),
),
)
See examples/ and tests/test-render-basic.typ for more combinations and edge cases.
Notes
- Scorify defaults to the Bravura SMuFL font and bundled Bravura metadata.
- Alternate SMuFL fonts may need spacing adjustments depending on their metadata quality.
- Core spacing constants live in
src/constants.typ. - The library requires Typst
0.14.0+and CeTZ0.4.2.
Contributing
Bug reports, feature requests, and pull requests are welcome in the official repository.
License
MIT - see LICENSE.