No description
Find a file
Me Here 17492fdfb0 Single Save button by Tap (disabled when nothing loaded); per-entry + Clear-all history delete; bigger display text
- Move per-item 💾 out of set-list rows into one 💾 Save next to Tap; it
  overwrites the loaded item and is disabled when no item is loaded.
- History list: red ✕ on hover deletes one session; Clear all wipes
  history for the current item only.
- Bump dark-display text again (BPM 80px, timers 26px, status 19px);
  widen the display column to fit.
- README: play key Space -> P.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:45:26 -05:00
deploy.sh Per-lane enable (replacing mute), feature boxes, set-list continue mode, external QR 2026-05-24 18:47:16 -05:00
index.html Single Save button by Tap (disabled when nothing loaded); per-entry + Clear-all history delete; bigger display text 2026-05-25 07:45:26 -05:00
README.md Single Save button by Tap (disabled when nothing loaded); per-entry + Clear-all history delete; bigger display text 2026-05-25 07:45:26 -05:00
release.sh Add versioning: VERSION file, dev/formal stamping, release.sh 2026-05-24 16:59:31 -05:00
VERSION Add versioning: VERSION file, dev/formal stamping, release.sh 2026-05-24 16:59:31 -05:00

Stackable Metronome

A browser polymetric groove trainer / metronome — and the design mockup for a Raspberry Pi Pico hardware build. Stack as many "meter lanes" as you like; each is its own little metronome with a grouping, subdivision, drum voice and per-beat pattern. Layering lanes produces polymeter and true ratio polyrhythm.

Live: https://metronome.varasys.io

It's a single page (index.html) plus a vendored QR library — no build step, no framework. State (presets, set lists, practice log, theme) lives in localStorage.

Features

  • Meter lanes — grouping (odd meters), subdivision, GM drum voice, perbeat on/off pattern (rests), mute, live measure counter.
  • Polyrhythm — a perlane poly toggle fits a lane's beats evenly into lane 1's bar (e.g. 5over4, 3over2).
  • Practice — gap/mute trainer (play N / mute M bars) and a tempo ramp with a start BPM and signed step.
  • Set lists — named, ordered lists of saved setups; ▶ loads + starts an item, N advances; each play is logged for crossday comparison.
  • Sharing — copy a link (with QR) to your current settings or a whole set list.
  • Theming — System / Light / Dark.

The share language

A compact, humanreadable text encodes a full configuration (a patch). It's what goes in a share link, and you can handwrite or edit it.

Patch grammar

v1 ; t<bpm> [; vol<pct>] ; <lane> ; <lane> … [; tr<play>/<mute>] [; rmp<start>/<step>/<every>]
Token Meaning Example
v1 format version (always first) v1
t<bpm> tempo t120
vol<pct> master volume 0100 vol70
tr<play>/<mute> gap trainer: play N bars, mute M tr2/2
rmp<start>/<step>/<every> tempo ramp: start BPM, ±step, every N bars rmp80/5/4
<lane> a meter lane (see below) kick:4

Tokens are joined with ;. tr and rmp are omitted when off.

Lane grammar

<sound> : <grouping> [ / <sub> ] [ = <pattern> ] [ ~ ] [ ! ]
  • sound — one of: beep, kick, snare, rim, clap, hatClosed, hatOpen, ride, crash, tomLow, tomMid, tomHigh, tambourine, cowbell, woodblock, claves, jamblock (unknown → beep).
  • grouping — beats per bar, optionally grouped for odd meters: 4, 3, 2+2+3. The first beat of each group is accented.
  • /sub — subdivision (clicks per beat): 1 quarter (default), 2 eighth, 3 triplet, 4 sixteenth, 6 sextuplet. Omit for quarter.
  • =pattern — perbeat on/off as x/., length = beats per bar. Omit = all on. e.g. =.x.x puts a backbeat on 2 & 4.
  • ~ — polyrhythm: fit this lane's beats evenly into lane 1's bar.
  • ! — mute the lane.

Examples

Patch / lane What it is
kick:4 kick on 4 quarter beats
snare:4=.x.x snare backbeat (2 & 4)
hatClosed:4/2 eighthnote hihats
claves:5~ 5 evenly across lane 1's bar (5over4 if lane 1 is 4)
kick:2+2+3=x..x..x 7/8, kick on each group start
cowbell:3+2/2 5/4 grouped 3+2, eighth subdivision
Full: v1;t120;kick:4;snare:4=.x.x;hatClosed:4/2;tr2/2 backbeat groove with gap trainer

In URLs

  • Settings: …/#p=<patch> — readable, e.g. …/#p=v1;t120;kick:4;claves:5~
  • Set list: …/#sl=<base64url> — a JSON {title, description, items[]} where each item's config is a patch string. Used because titles/notes are free text.

Opening such a link applies the settings (or imports the set list) on load, then clears the hash so a refresh won't reimport.

Sharing

In the setlist panel's menu:

  • Share settings link / Share setlist link open a dialog with the link to Copy or Open.
  • QR ↗ opens a thirdparty QR service (api.qrserver.com) with the link in its URL so you can scan it on a phone. A banner warns you it's external — confirm the QR decodes to the shown link before trusting it. (No QR is generated locally.)
  • Export all / Import file back up presets + set lists + logs as a JSON file.

Keyboard shortcuts

Key Action
P play / stop
T tap tempo
/ tempo ±1 (Shift = ±10)
A add meter lane
19 mute lane N
R toggle the setlist panel
N next setlist item
? shortcuts help
Esc close dialog / panel

Versioning

VERSION holds the formal version. deploy.sh stamps the served page:

  • Formal — a clean commit tagged v<VERSION>X.Y.Z.
  • Dev — anything else → X.Y.Z-dev.<utc-timestamp>.<short-sha>[.dirty].

Cut a release with ./release.sh [X.Y.Z] (bumps VERSION + tags v<VERSION>), then push the tag and deploy.

Deploy

./deploy.sh copies index.html (versionstamped) into the Caddy web root and smoketests the live URL. No restart needed (file_server picks up changes immediately).

Files

File Purpose
index.html the whole app
deploy.sh publish to the Caddy web root
release.sh tag a formal version
VERSION formal version string