Notation — Session Layer
The notation feature has two complementary modes:
- Authored —
notation(...)cues written into score.svg ids (seenotation()cue for DSL details). - Session — notation cues created live in the browser via the topbar
+ Notation (
✛) button, persisted per-project tonotation-cues.jsonand synced across connected clients.
Both modes share the same renderer, edit-mode tools, and persistence plumbing. This page covers session-layer creation and the full edit-mode interaction surface.
Creating a Notation Cue
Click the ✛ topbar button to enter create mode, then click anywhere on the score where you want a fragment to appear. A modal opens with these fields:
| Field | Description |
|---|---|
| Layout | Single, Piano, Organ, Quartet, Custom (multi-stave) |
| Count | Number of notes |
| Low / High | Pitch range (the algorithm picks notes inside this range) |
| System | chromatic, scale, microtonal, random |
| Scale | When system = scale, choice of 17 scales (major, minor, modes, octatonic, hexatonic, blues, …) |
| EDO | When system = microtonal, equal divisions per octave (12, 24, 31, 53, …) |
| Direction | ascending, descending, random, serial, mirror, arch, bidirectional |
| Transposition | Instrument key (e.g. Bb for clarinet) |
| Key signature | Concert key (C, Eb, F#, …) |
| Clef | treble or bass |
| Staff height / spacing | Render-time geometry |
| Boxed | Wraps the cue in a rectangle outline |
| Playhead | Default for playhead-pass OSC trigger (per-note overrides via the Audio tab) |
| OSC address | Optional override of the auto-generated address |
Multi-stave layouts (piano / organ / quartet) generate one cue with multiple staves rendered as a single coordinated system, with one shared extent line and bounding box wrapping the whole thing.
Edit Mode
Click the topbar 🎼 button to toggle edit mode. While active:
- Hosts highlight on hover
- Drag handles, palettes, and double-click affordances are exposed
- Click triggers (OSC, sample) are suppressed so they don't fire while you're editing
- The 🎼 button is fastest to toggle when both hands are on the keyboard — there's currently no global keyboard shortcut
All edit-mode changes are persisted live: positions to localStorage,
session cue specs to notation-cues.json via REST + WebSocket
broadcast.
Cue-level Selection
| Action | Effect |
|---|---|
| Click a clef | Select that whole cue (single selection) |
| Shift-click a clef | Add to / remove from cue selection |
| Drag from empty score area | Marquee-select cues whose bbox intersects the rectangle |
| Shift + drag | Marquee adds to existing selection |
| Drag a clef of a selected cue | Move all selected cues together |
| Keystroke | Effect |
|---|---|
Ctrl/Cmd + C |
Copy selected cues to in-memory clipboard |
Ctrl/Cmd + X |
Cut (copy + delete) |
Ctrl/Cmd + V |
Paste with offset |
Ctrl/Cmd + D |
Duplicate (copy + paste in one step) |
Delete / Backspace |
Delete selected cues (or selected glisses if any) |
Pasted cues retain their notehead styles, spacing, extent, articulations and dynamics, gliss connections, and per-note samples.
Per-note Editing
| Action | Effect |
|---|---|
| Drag a notehead horizontally | Adjust that note's spacing |
| Shift-click a notehead | Toggle multi-select |
| Hover a notehead | Tooltip shows sounding (and written) pitch name |
| Click a notehead (out of edit mode) | Fires OSC and plays sample if assigned; flashes red |
| Double-click a notehead | Opens the note popup — see below |
| Shift-drag from a notehead | Draws a gliss line (release on another notehead to snap, in space to leave free-floating) |
The Note Popup (Tabbed)
Double-click any notehead while in edit mode. A popup opens with four tabs, each operating on whichever notes are currently selected (single note if you double-clicked one note; multiple if a multi-select was active):
Notehead
A grid of SMuFL notehead glyphs:
filled, half, whole, x, diamond (artificial harmonic),
diamondOpen (natural harmonic), square, slash.
Click to apply to all targeted notes. Currently active style is highlighted.
Articulation
18 SMuFL articulation marks. Click to toggle across all targeted notes — if the mark is on every targeted note it will be removed; otherwise it will be added everywhere it's missing. Multiple marks per note stack vertically above the staff.
Available: staccato, staccatissimo, accent, accentStaccato,
tenuto, portato, tenutoAccent, marcato, marcatoStaccato,
stress, unstress, laissezVibrer, fermata, fermataShort,
fermataLong, fermataVeryLong, harmonic (string harmonic circle),
snapPizz.
Dynamic
22 SMuFL dynamic glyphs in soft → loud order, plus compound / accent dynamics. Click to apply; click the same one again to clear; or use the clear button.
Available: n (niente), ppppp, pppp, ppp, pp, p, mp, mf,
f, ff, fff, ffff, fffff, fp, pf, sf, sfp, sfz,
sffz, rf, rfz, fz.
Audio
The Audio tab has three sections:
Sample assignment
- No sample assigned /
▶ <path>— current state for the first selected note - browse… — opens a file picker over the project's
/audiodirectory; click a row to assign, or use the inline ▶ button to preview without assigning - play — preview the currently-assigned sample
- clear — remove the assignment
When a note has a sample, clicking it (outside edit mode) plays the
sample through the same engine as audio(…) cues, with poly:0 so
rapid retriggers stack.
This note — per-note OSC trigger flags
- ☐ click → OSC (override)
- ☐ playhead → OSC (override)
The checkboxes show the effective state. Toggling sets a per-note
override (marked with (override) next to the label). A ↺ reset to stave default link appears whenever any flag is overridden — clicking
it clears the per-note override back to inherited.
Stave default — applies to every note in the cue (or stave, in multi-stave systems) that doesn't have its own override.
- ☐ click → OSC
- ☐ playhead → OSC
Both default to true. Disabling playhead → OSC at the stave level
mutes the entire cue from playhead-trigger OSC; per-note overrides can
still selectively re-enable individual notes.
Glisses
Shift-drag from a notehead to draw a gliss line. Release:
- On another notehead → gliss snaps with that note as endpoint
- In empty space → gliss is saved with a free
{x, y}endpoint
Click a gliss to select; shift-click for multi-select. With glisses selected, Delete removes them.
Drag the midpoint of a gliss to add a breakpoint — the line bends through that point. Drag any breakpoint to reposition; drag onto an endpoint to merge.
Double-click a gliss to open the gliss style picker:
straight, wavy, arrow, dotted, dashed, trill, portamento.
In multi-stave systems, glisses can connect notes across staves
(piano-style cross-stave gestures, organ pedal-to-manual, quartet
voice-to-voice). The endpoint addressing is { staveIdx, index }.
Stave / System Handles
In edit mode, each cue exposes a small set of right-edge handles:
- Blue handle at the right of each stave's last staff line — drag horizontally to extend or contract that stave's staff lines.
- Orange handle to the right of the cue — drag horizontally to define the extent line (sustain bar). Drag past the staff edge to the left to remove the extent. Double-click for the extent style picker (block / thin / dashed / wavy / dottedTrill / arrowed).
For multi-stave systems (piano / organ / quartet), there is one orange extent handle spanning every stave — the extent applies to the whole system. The extent style picker doubles as a box around system toggle.
In multi-stave systems with two or more staves, a thin gap drag appears between adjacent staves — drag vertically to widen or narrow the inter-stave gap.
DSL Editor (Clef Double-click)
Double-click a clef (not a notehead) to open a textarea with the cue's parsed DSL. Edit the string and apply — the renderer re-parses and re-renders. Reset reverts to the original; Cancel discards.
This is the fast path for changes that aren't covered by the popup
tabs (e.g. tweaking keysig, transp, or directly editing the
pitches: list).
Undo / Redo
| Keystroke | Effect |
|---|---|
Ctrl/Cmd + Z |
Undo |
Ctrl/Cmd + Shift + Z |
Redo |
Ctrl/Cmd + Y |
Redo (Windows convention) |
History is a cursor-model stack capped at 100 entries, snapshotting both the session cue specs (file-backed) and per-uid notation overrides (localStorage). A baseline is captured after the project's notation cues finish loading, so the first edit always has somewhere to undo back to.
Persistence
Two complementary stores:
notation-cues.json— per-project, contains the spec of every session-created cue (id, position, layout, pitches, base parameters). Synced across clients via WebSocket; written via REST.localStorage::oscilla.notationPositions.v1::<project>— per-uid overrides layered on top of the spec at render time: per-note spacing, notehead styles, extent x / style, articulations, dynamics, samples, OSC click / playhead flags, glisses (and breakpoints), per-stave width, system extent, system box.
Authored cues (those in score.svg) only use the localStorage overrides
— their base spec lives in the SVG id and is never modified by the
editor.
Multi-client Sync
Both stores broadcast to every connected client of the same project:
notation_cues_save(WS) →notation_cues_response(broadcast)- Drag positions sync via the existing drag broadcast plumbing
A late-joining client requests both on connect, so cues that were created before they joined appear immediately.
Tips
- Don't be afraid of the marquee — drag from an empty area of the score to lasso a group of cues; it's the fastest way to copy/paste an entire phrase into a different page.
- The Audio tab is also where you disable a note from triggering OSC. Set the stave default to ☐ and the cue becomes silent on click; re-enable individual notes via per-note overrides for a sparse trigger pattern.
- For piano / organ writing, double-click a stave's clef to open the DSL editor — you can reorder pitch lists or adjust spacing per stave without leaving edit mode.
- If a session cue isn't appearing on reload, the server hasn't yet
picked up the WS save — wait a beat or check the browser console for
[notationCreate] save failed:warnings.
Tip: use ← → or ↑ ↓ to navigate the docs