metronome/README.md
Me Here 1f957b7630 Update README: fix stale claims found in code review
- Drop the false "vendored QR library" line; state it's a single
  self-contained, zero-dependency file and document offline use.
- Fix keyboard shortcuts to match the in-app help: remove the
  non-existent R toggle, add Alt+↑/↓ (reorder), 1–9 toggles a lane.
- "GM drum voice" → synthesized; set-list items load on click (no ▶
  auto-start); QR is an external service; clarify release.sh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:05:34 -05:00

166 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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, selfcontained `index.html`**zero dependencies**: no framework,
no build step, no bundled or CDN libraries, and nothing fetched at runtime. State
(set lists, the practice log, theme and UI preferences) lives in `localStorage`.
Because nothing loads from the network, you can save the page (`Ctrl`/`⌘`+`S`) and
open it straight from disk to run fully offline. One catch from a local `file://`:
the browser may not persist `localStorage` between sessions, so use **Export all**
(setlist **⋯** menu) to back up your work.
## Features
- **Meter lanes** — grouping (odd meters), subdivision, a synthesized drum/percussion 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; click an item to load it
(it switches live if you're already playing), **N** loads the next; each play is
logged for crossday comparison.
- **Sharing** — copy a link to your current settings or a whole set list (with an
optional QR generated by an external service).
- **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: `1` quarter (default), `2` eighth, `3` triplet,
`4` sixteenth, `6` sextuplet. This also sets how many **pads** each beat splits
into (a beat becomes `sub` individuallytoggleable steps). Omit for quarter.
- **`=pattern`** — per**step** on/off as `x`/`.`, length = beats per bar × `sub`
(one char per pad). Omit = all on. e.g. `4=.x.x` is a backbeat on 2 & 4;
`4/4=x..x..x.x...x...` is a sixteenthgrid pattern. A short pattern whose length
equals just the beat count is still accepted and expanded across each beat's
subdivisions (backcompat).
- **`~`** — 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 your set lists and practice log as a JSON
file (a legacy `presets` field is included for backward compatibility).
## Keyboard shortcuts
| Key | Action |
|-----|--------|
| `Space` | play / stop (works everywhere except while typing in a text field) |
| `T` | tap tempo |
| `↑` / `↓` | tempo ±1 (`Shift` = ±10) |
| `A` | add meter lane |
| `N` | load next setlist item |
| `Alt`+`↑` / `Alt`+`↓` | reorder the selected setlist item |
| `1``9` | enable / silence lane 19 |
| `?` | shortcuts help |
| `Esc` | close the help / share dialog |
(Arrow keys are left alone while a slider or dropdown is focused, so they still adjust it.)
## 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]` — the optional arg bumps & commits
`VERSION`; it then tags the current commit `v<VERSION>` (requires a clean tree).
Push the tag, then 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 |
| `LICENSE` | GNU AGPL v3 license text |
## License
Copyright (C) 2026 Varasys.
This program is free software: you can redistribute it and/or modify it under
the terms of the **GNU Affero General Public License** as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version. See [`LICENSE`](LICENSE) for the full text.
Because the app is served over a network, the AGPL's §13 applies: anyone
interacting with a hosted instance must be able to get its source. The repo
link in the inapp **?** help satisfies this.