metronome/README.md
Me Here 9cb7c2c193 Per-step pad grid (beats × subdivision); drop confusing Sig dropdown; help: repo link + offline note
- Pad row now shows beatsPerBar × subdivision pads, each individually
  toggleable (the subdivision control sets pad resolution). Subdivision
  pads render smaller; downbeats labeled; group/beat gaps preserved.
- Mask (beatsOn) is now per-step; playhead tracks the current step.
  recomputeLane remaps on grouping/subdivision change and migrates legacy
  per-beat masks (saved data, short share patterns) by expanding across subs.
- Share language: =pattern is now per-step (len = beats × sub); short
  per-beat patterns still accepted and expanded. README updated.
- Removed the Sig time-signature preset dropdown (confusing vs subdivision).
- Help dialog: link to git.varasys.io/VARASYS/metronome + note that it's a
  single-page app you can save & run offline, but file:// won't auto-save
  the set list (export a backup).

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

141 lines
5.4 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 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: `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 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 |
| `1``9` | 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 |