metronome (with autostart support)
Triggers a visual and/or audible metronome marker at the cue’s location or a specified target: element.
The metronome can follow scrolling objects, stay fixed to the viewport, and optionally send OSC messages.
The metronome can also autostart, meaning it runs automatically when the SVG is loaded in either page mode or scroll mode without needing a cue trigger crossing.
Syntax
metro(bpm:<number>,beats:<number>,visual:<type>,audio:<0|1>,
position:<fixed|scrolling>,osc:<0|1>,uid:<string>,
target:<uid>,hideTarget:<0|1>,showcount:<0|1>,hold:<seconds>,
colour:<csscolour>,size:<pixels>,trig:<auto|edge>)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| bpm | number | 120 |
Beats per minute. Controls metronome speed. |
| beats | number | 4 |
Number of beats per cycle. |
| visual | string | "circle" |
Visual style (circle, hex, future shapes). |
| audio | 0 / 1 |
0 |
Enables click sound. |
| position | "fixed" / "scrolling" |
"fixed" |
Follow-scrolling or fixed overlay. |
| osc | 0 / 1 |
0 |
Publishes OSC beat messages. |
| uid | string | "default" |
Unique metronome identifier. |
| target | uid | — | Anchor element (scrolling mode only). |
| hideTarget | 0 / 1 |
1 |
Hide target element when starting. |
| showcount | 0 / 1 |
1 |
Show count number inside shape. |
| hold | seconds | 0 |
Stop after defined duration. |
| colour | CSS color | "red" |
Circle colour. |
| size | pixels | 50 |
Circle diameter. |
| trig | "auto" / "edge" |
"edge" |
Whether to autostart or trigger when playhead crosses. |
Behaviour
-
Runs as overlay DOM element (not part of the SVG).
-
Follows scrolling or stays screen-fixed depending on
position:. -
Supports audio click and OSC output.
-
Clicks in phase across clients. The beat grid is anchored to the cue's position on the score timeline, and that score time is turned into a wall-clock instant through the server's transport clock: each client measures its clock offset to the server over the WebSocket (NTP-style ping/pong, a few ms of error on a LAN), so every machine derives the same grid whatever its own playhead is doing. Metros started by the playhead click together, a late joiner lands on the beat, and a retrigger or rewind re-derives the same phase rather than starting a fresh one. (Before the clock is ready the grid falls back to the local playhead; a metro triggered without a timeline position — e.g. from the live console — free-runs from its trigger moment.)
To check two machines: open the browser console on both. When the metro starts each prints
[cue:metro] <uid> grid origin from server transport clock — server time N ms (clock offset …, rtt …). The sameNon both means the same grid; what remains is the offset error (about half the rtt) plus each device's audio output latency.serverClock.debugInfo()in the console shows the live offset and round trip. -
Timing is a lookahead scheduler on the shared audio clock, not the frame loop: beats are frame-rate independent, keep going while the tab is in the background, and resync cleanly instead of "catching up" in a burst after a stall. One metro per
uid— retriggering restarts it. -
Supports multiple concurrent metronomes (UID required).
-
Can target another SVG element and hide it on activation.
Autostart Mode (NEW)
When is a metronome autostarted?
Whenever the cue contains:
trig:auto
Example:
cue:metro(bpm:90,visual:hex,uid:m21,trig:auto)
When does autostart happen?
- In page overlay mode (during PDF-style view)
- In scroll mode (timeline view)
- After the SVG and DOM are initialized
- Before the animation clock starts
What does autostart actually do?
Autostart metronomes fully initialise as if they had been triggered by the scrolling playhead:
- Overlay DOM is created
- Beat scheduler starts
- UI is visible
- OSC messages begin
- Audio (if enabled) clicks on beat 1,2,3,4…
Multiple autostarts supported
Each metronome UID becomes its own independent instance.
Example cues
| Description | Example |
|---|---|
| Autostart metronome at load | metro(bpm:90,visual:hex,trig:auto,uid:m1) |
| Two autostarts | metro(bpm:70,trig:auto,uid:A) + cue:metro(bpm:110,trig:auto,uid:B) |
| Standard (on crossing), no autostart | metro(bpm:100,visual:circle,uid:M) |
| Scroll-following autostart | metro(bpm:120,position:scrolling,target:x1,trig:auto,uid:hex1) |
OSC Message Format
With osc:1 every beat sends one message to the OSC out port:
/oscilla/metro/<uid> beat(int) bpm(float)
/oscilla/metro/m21 3 90.0
Example receivers for Pd, Max and SuperCollider: OSC Output.
Implementation Notes (for developers)
- Autostart hooks run after DOM insertion and SVG setup.
- Autostart routines are separate from cue crossing logic:
- No tracking in
triggeredCues - No playhead position checks required
- No tracking in
- Overlay visibility ensured via:
div.style.zIndex = "99999";
Known Limitations
- Multiple autostarts in high BPM (>200) may need scheduler improvements.
- OSC feedback not currently aware of phase differences between UIDs.
- Appearance position is based on CSS absolute, not SVG hitbox.
Driving other cues
A metronome publishes its pulse, so the rest of the score can move in time with it rather than only hearing it:
| Channel | Meaning |
|---|---|
beat |
which beat of the bar, 1-based |
phase |
position through the bar, 0–1 |
bar |
bars since it started |
phase is continuous, not stepped — it is what you want for anything that
should turn or travel in time:
metro(uid:click, bpm:96, beats:4)
rotate(uid:r1, dir:1, dur: follow(click.phase, 4, 0.5))
beat steps 1, 2, 3, 4 and is the musical fact; bar is a counter that keeps
climbing. All three come off the same shared grid the clicks are scheduled on,
so they stay in phase across the network the way the clicks do.
Demo project
See demo-transport: overlay with click, visual:self flashing a target, and a scrolling hex — with the network-synced grid.
http://localhost:8001/?project=demo-transport
Tip: use ← → or ↑ ↓ to navigate the docs