notation(...) — Timepoint Notation
Renders a fragment of staff-based notation directly inside an SVG element. Notes are spaced proportionally in time rather than rhythmically — the horizontal position of each notehead is what matters; durations are conveyed by extent lines, fermatas, and gaps. This style of notation is sometimes called timepoint notation or proportional notation; the same approach underpins works by Brown, Cardew, Lutosławski, and Penderecki.
A notation() cue can fire OSC on click and on playhead-pass, draw glisses
between noteheads (including across staves in multi-stave systems), and
carry articulations, dynamics, and per-note sample assignments.
For interactive creation and editing in the browser, see Timepoint Notation in the App.
Quick Start
Give an SVG element an id like:
notation(pitches:'~A4 ~C5 ~Eb5 ~G5')
Open in the browser → a treble clef and four noteheads render inside the element's bounding box, evenly spaced.
Syntax
notation(
pitches:'~Bb3 ~Eb5 ~G4 ~Eb3',
clef:treble,
keysig:Eb,
transp:Bb,
height:34,
spacing:22,
extent:120,
extentStyle:wavy,
boxed:1,
playhead:1,
click:1,
osc:/oscilla/notation/myUid/note,
uid:myUid
)
Parameters
| Key | Default | Description |
|---|---|---|
pitches (required) |
— | Space-separated list of pitches (see Pitch Syntax below) |
clef |
treble |
treble or bass |
keysig |
— | Major key for key signature (e.g. Eb, G, F#); biases enharmonic spelling and renders accidentals |
transp |
C |
Instrument transposition (e.g. Bb, Eb, F) — input pitches are sounding, the staff shows the written pitch |
height |
40 | Staff height in SVG units |
spacing |
18 | Horizontal gap between noteheads |
extent |
— | X-position where the cue's "extent line" ends (sustain bar past the noteheads); also dragable in edit mode |
extentStyle |
block |
block, thin, dashed, wavy, dottedTrill, arrowed |
boxed |
false |
Draw a rectangle outline around the whole fragment |
boxRadius |
0 | Rounded corners on the bounding box |
boxPadding |
6 | Padding from staff edges to the box |
glisses |
— | Array of gliss objects (see Glisses below) |
articulations |
— | Map of { noteIdx: ["staccato","accent",…] } — see Articulations |
dynamics |
— | Map of { noteIdx: "mf" } — see Dynamics |
playhead |
(auto) | When non-false, the playhead crossing each notehead emits OSC. As of v… playhead-trigger is on by default; the per-note Audio tab in edit mode lets you override per-note or per-stave. |
click |
1 | When non-false, clicking a notehead emits OSC and plays its assigned sample (if any) |
osc / oscaddr |
/oscilla/notation/<uid>/note |
OSC address; {i} is replaced with note index, {s} with stave index in multi-stave systems |
uid |
(derived from id) | Stable identifier used for persistence of edit-mode overrides |
Pitch Syntax
~A4 → A above middle C
~Bb3 → B-flat below middle C
~F#5 → F-sharp above the staff
~Db5-31 → Db5, 31 cents flat (microtonal)
~A4+50 → A4, 50 cents sharp (quartertone)
60 → MIDI note number (middle C)
The ~ prefix is the conventional mark for a pitch literal. Cents
deviation is appended as +N or -N; the renderer draws the cents
value as a small superscript next to the notehead.
Pitches in pitches: are interpreted as sounding pitches — the
actual frequency the player should hear. If transp: is set, the staff
displays the corresponding written pitch (transposed up or down by
the instrument's transposition interval).
OSC Output
Each notehead can emit an OSC trigger. Two sources:
- Click: clicking a notehead in the score (outside edit mode)
- Playhead-pass: the score's playhead crossing the notehead's x-coordinate
Default address pattern: /oscilla/notation/<uid>/note
Args (single-stave): [noteIndex, midi, freqHz, cents, noteheadStyle, pitchName]
Args (multi-stave): [staveIndex, noteIndex, midi, freqHz, cents, pitchName]
Custom addresses can use {i} (note index) and {s} (stave index) —
quote the address when it contains them, oscaddr:'/synth/note/{i}', as bare
{ is not part of the DSL's address token
placeholders:
notation(pitches:'~C4 ~D4 ~E4', oscaddr:'/synth/note/{i}')
To enable / disable click and playhead triggers per-note or per-stave, use the Audio tab in the edit-mode popup (see Timepoint Notation in the App).
Glisses
Glisses (portamenti, slides, trill lines, custom contour lines) connect
two noteheads, or a notehead to a free-floating point. Persistence is
tied to uid; in edit mode, shift-drag from a notehead to draw one.
Authored DSL form:
notation(
pitches:'~A4 ~G5 ~D4',
glisses:[
{ from:{index:0}, to:{index:1}, style:straight }
]
)
| Style | Description |
|---|---|
straight |
Plain line (default) |
wavy |
Sinusoidal vibrato line |
arrow |
Line ending in an arrowhead |
dotted |
Dotted line |
dashed |
Dashed line |
trill |
Trill (tr + line) |
portamento |
Curved bezier |
Glisses can include intermediate breakpoints (breakpoints:[{x,y},…])
to bend through specific points; in edit mode, dragging the midline of a
gliss creates a breakpoint.
For multi-stave systems, gliss endpoints carry a staveIdx as well as
index:
glisses:[
{ from:{staveIdx:0, index:3}, to:{staveIdx:1, index:0}, style:wavy }
]
Articulations
Articulation marks render above (or below) each anchored note. Multiple marks on one note stack vertically.
notation(
pitches:'~C5 ~D5 ~E5',
articulations:{ 0:["staccato","accent"], 2:["fermata"] }
)
Available marks (SMuFL Bravura glyphs):
staccato, staccatissimo, accent, accentStaccato, tenuto,
portato, tenutoAccent, marcato, marcatoStaccato, stress,
unstress, laissezVibrer, fermata, fermataShort, fermataLong,
fermataVeryLong, harmonic, snapPizz.
In edit mode, dblclick a notehead → Articulation tab to toggle marks across all selected notes.
Dynamics
A single dynamic sits below the staff at the anchored note's x-position.
notation(
pitches:'~C5 ~D5 ~E5',
dynamics:{ 0:"p", 2:"f" }
)
Available dynamics:
n (niente), ppppp, pppp, ppp, pp, p, mp, mf, f, ff,
fff, ffff, fffff, fp, pf, sf, sfp, sfz, sffz, rf,
rfz, fz.
In edit mode, dblclick a notehead → Dynamic tab.
Per-Note Samples
Each note can be assigned an audio sample to play on click and / or
playhead-pass. Samples are picked from the project's /audio directory
via the popup's Audio tab (see
Timepoint Notation in the App).
The sample is played through the same engine as audio(…) cues, so it
inherits gain and speed but currently bypasses FX routing.
Multi-Stave Systems
Piano, organ, and string-quartet layouts use notationSystem (see
in-app notation docs) — multiple staves rendered as a single coordinated
group with one shared extent line and bounding box spanning every
stave. Cross-stave glisses use staveIdx in their endpoint addressing.
Multi-stave systems are usually created via the topbar + Notation
button, but can also be authored in the score svg as a JSON spec.
Examples
Simple progression:
notation(pitches:'~C4 ~D4 ~E4 ~F4 ~G4', height:32, spacing:24)
Bb clarinet (sounding pitches), with Eb major key signature:
notation(pitches:'~Bb3 ~D4 ~F4 ~Bb4', transp:Bb, keysig:Eb)
Microtonal cluster:
notation(pitches:'~A4 ~A4+33 ~A4+67 ~A4+100', height:36)
Sustained tones with extent line:
notation(
pitches:'~G4 ~Bb4 ~D5',
extent:200,
extentStyle:wavy,
spacing:30
)
Boxed cluster with playhead-trigger OSC:
notation(
pitches:'~C4 ~D4 ~Eb4 ~F#4 ~G4',
boxed:1,
boxRadius:4,
playhead:1,
oscaddr:'/synth/cluster/{i}'
)
Edit-Mode Behaviour
When the topbar 🎼 (notation edit) toggle is on, every notation() cue
gains drag handles and double-click affordances. Per-note overrides
(positions, notehead styles, articulations, dynamics, samples, OSC flags)
are persisted under the cue's uid in localStorage and (for session
cues) in notation-cues.json. The score svg itself is never modified.
See Timepoint Notation in the App for the full set of edit-mode interactions.
Demo
See demo-score-follow — a reading line playing a stave note by note.
http://localhost:8001/?project=demo-score-follow
Tip: use ← → or ↑ ↓ to navigate the docs