- Concepts is now the landing (/): index.html is the form-factor gallery with the LIVE widget embedded in every box (editor/teacher/stage/micro/showcase/initial), on the shared header/footer. concepts.html retired; every "Concepts" link → /. - New shared chrome partials src/header.html, src/footer.html, src/chrome.js (assembled by build.sh) + .site-foot / details.spec styles in base.css. Applied to the landing + showcase this pass. - Showcase redesign per spec: the pendulum bar IS the display — each lane's subdivisions/accents ride along the rod as moving RGB light (all meters combined); transparent outside the body (no black window); a printed tempo scale on the vertical axis with a draggable weight to set tempo; start is an external button (the real unit starts when lifted from its holder). Next pass: roll the shared header/footer onto the remaining pages (incl. the editor header-above-toolbar), merge Open=Info into one page per form factor with the expandable Info & BOM, and add teacher-style dimensioned views to Stage/Micro/Showcase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
267 lines
14 KiB
Markdown
267 lines
14 KiB
Markdown
# VARASYS PolyMeter
|
||
|
||
A small **website** built around one **polymetric groove trainer / metronome** engine.
|
||
A landing page is the front door; the main app is the **PE‑1 PolyMeter Editor** — a full
|
||
web app where you stack as many "meter lanes" as you like, each its own little metronome
|
||
with a grouping, subdivision, drum voice and a per‑step pattern with accents. Layering lanes
|
||
produces polymeter and true ratio polyrhythm. The same engine drives an ever‑expanding library
|
||
of **form‑factor concepts** (idealized and buildable hardware mockups) and ships as an
|
||
**embeddable widget** anyone can drop into their own page.
|
||
|
||
**Live:** https://metronome.varasys.io · **Source:** https://codeberg.org/VARASYS/metronome
|
||
|
||
Every **deployed page is a single, self‑contained `.html` file** — **zero dependencies**:
|
||
no framework, no CDN libraries, nothing fetched at runtime. They're assembled by a small
|
||
build step (`build.sh`) that inlines a shared engine, the seed set lists, base styling and
|
||
the audio samples + brand assets (kept in `assets/`) into each page, so the sources stay lean.
|
||
State (set lists, the practice log, theme and UI preferences) lives in `localStorage`.
|
||
|
||
## Pages
|
||
|
||
| URL | What |
|
||
|-----|------|
|
||
| [`/`](https://metronome.varasys.io/) `index.html` | **Concepts** — the landing / form‑factor gallery; each box embeds the live widget |
|
||
| `/editor.html` | **PE‑1 — PolyMeter Editor** (the main app) |
|
||
| `/player.html` | **PM‑1 Initial** — idealized concept device (full display + set‑list nav, theme, fullscreen "stage" view) |
|
||
| `/teacher.html` | **PM‑1 Teacher** — studio / lesson console (colour TFT, arcade buttons, 1/4″ instrument pass‑through with analog click injection) |
|
||
| `/stage.html` | **PM‑1 Stage** — foot‑pedal stompbox (two footswitches, expression‑pedal in, RGB beat light, instrument pass‑through) |
|
||
| `/micro.html` | **PM‑µ Micro** — inline practice bar (instrument in / out pass‑through, clickable thumb‑roller, 14‑segment display) |
|
||
| `/showcase.html` | **PM‑S Showcase** — pyramid display piece with an RGB‑light pendulum + per‑lane subdivision/accent light rows |
|
||
| `/info-editor.html`, `/info-initial.html` | purpose pages (web app / concept — no BOM) |
|
||
| `/info-teacher.html`, `/info-stage.html`, `/info-micro.html`, `/info-showcase.html` | purpose **+ priced BOM** (buildable hardware only) |
|
||
| `/embed.html` · `/embed.js` | embed docs and the drop‑in loader |
|
||
|
||
Each page carries the same VARASYS header (logo + tagline, nav, theme toggle). The editor
|
||
also shows a subtle live **program string** of what's loaded — editable, with copy/paste —
|
||
under the app (press `Enter` or paste to apply; see [the share language](#the-share-language)).
|
||
|
||
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**
|
||
(set‑list **⋯** menu) to back up your work.
|
||
|
||
## Features
|
||
|
||
- **Meter lanes** — grouping (odd meters), subdivision (incl. swing), a drum/percussion
|
||
voice, per‑**step dynamics** (accent / normal / ghost / mute), mute, live measure counter.
|
||
- **Sounds** — a sampled acoustic kit plus synthesized **808 / 909** and electronic voices;
|
||
click each pad to set its dynamics; pick a *swing* subdivision for a triplet feel.
|
||
- **Polyrhythm** — a per‑lane *poly* toggle fits a lane's beats evenly into lane 1's
|
||
bar (e.g. 5‑over‑4, 3‑over‑2).
|
||
- **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; **cue** across lists and commit
|
||
on a bar/beat boundary with no audible gap (see **Live performance**); each play is
|
||
logged for cross‑day comparison.
|
||
- **Sharing** — copy a link to your current settings or a whole set list.
|
||
- **Theming** — System / Light / Dark.
|
||
|
||
## The share language
|
||
|
||
A compact, human‑readable text encodes a full configuration (a *patch*). It's what
|
||
goes in a share link, and you can hand‑write or edit it.
|
||
|
||
### Patch grammar
|
||
|
||
```
|
||
v1 ; t<bpm> [; vol<pct>] [; cd<sec>] [; b<bars>] ; <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 0–100 | `vol70` |
|
||
| `cd<sec>` | time countdown, seconds (auto-advance with Continue) | `cd60` |
|
||
| `b<bars>` | segment length in bars (auto-advance with Continue) | `b16` |
|
||
| `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** — the acoustic kit: `beep`, `kick`, `snare`, `rim`, `clap`, `hatClosed`,
|
||
`hatOpen`, `ride`, `crash`, `tomLow`, `tomMid`, `tomHigh`, `tambourine`, `cowbell`,
|
||
`woodblock`, `claves`, `jamblock` (kick, snare, closed‑hat, crash, toms, tambourine,
|
||
cowbell, woodblock and claves play embedded CC0 samples; `beep`, `clap`, `rim`,
|
||
`hatOpen` and `ride` stay synthesized — VCSL has no clean source for those); plus
|
||
synthesized drum machines —
|
||
`kick808 snare808 clap808 hat808 openHat808 cowbell808 tom808` and
|
||
`kick909 snare909 clap909 hat909 ride909 crash909`. Unknown → `beep`.
|
||
- **grouping** — beats per bar, optionally grouped for odd meters: `4`, `3`,
|
||
`2+2+3`. Groups get a visual divider; accents are per‑step (see `=pattern`).
|
||
- **`/sub`** — subdivision: `1` quarter (default), `2` eighth, `3` triplet,
|
||
`4` sixteenth, `6` sextuplet. This also sets how many **pads** each beat splits
|
||
into. Append **`s`** for **swing** on even subdivisions — `2s` (swung eighths) or
|
||
`4s` (swung sixteenths) delay the off‑beats to a triplet (2:1) feel. Omit for quarter.
|
||
- **`=pattern`** — per‑**step dynamics**, one char per pad: **`X`** accent, **`x`**
|
||
normal, **`g`** ghost (soft), **`.`** mute (rest). Length = beats per bar × `sub`. Omit to get the
|
||
default — the first step of **each beat** accented, the rest normal (click a pad in
|
||
the UI to cycle accent → normal → ghost → mute). e.g. `4=.X.X` accents the backbeat (2 & 4);
|
||
`4/2s` is swung eighths with the default accents. (Legacy `x`/`.` on/off patterns and
|
||
short beat‑count patterns still parse.)
|
||
- **`~`** — 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` | accented snare backbeat (2 & 4) |
|
||
| `hatClosed:4/2` | eighth‑note hi‑hats (downbeat of each beat accented) |
|
||
| `ride:4/2s` | **swung** eighth‑note ride |
|
||
| `claves:5~` | 5 evenly across lane 1's bar (5‑over‑4 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 re‑import.
|
||
|
||
## Sharing
|
||
|
||
In the set‑list panel's **⋯** menu:
|
||
- **Share settings link** / **Share set‑list link** open a dialog with the link to
|
||
**Copy** or **Open**. The link encodes everything in the URL — nothing is uploaded.
|
||
- **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).
|
||
|
||
## Embedding
|
||
|
||
Any form factor can be embedded in another page as a self‑sizing widget. Drop in a
|
||
container and the loader script — it builds an `<iframe>` to the chrome‑stripped
|
||
(`?embed=1`) page, preloads your config string, and auto‑resizes to the content:
|
||
|
||
```html
|
||
<div data-varasys-metronome="micro"
|
||
data-patch="v1;t120;kick:4;snare:4=.X.X;hatClosed:4/2"></div>
|
||
<script src="https://metronome.varasys.io/embed.js"></script>
|
||
```
|
||
|
||
- `data-varasys-metronome` — variant: `editor` · `initial` · `teacher` · `stage` · `micro` · `showcase`.
|
||
- `data-patch` — a [patch string](#patch-grammar) (maps to `#p=`); or `data-setlist`
|
||
for a set‑list code (maps to `#sl=`).
|
||
- `data-width` / `data-height` — optional initial size (default `100%` × `300px`;
|
||
height then tracks the widget, which posts `{type:'varasys-h', h}` to the parent).
|
||
|
||
Prefer your own iframe? `…/<variant>.html?embed=1#p=<patch>` works directly. Our own
|
||
[`info-*.html`](info-teacher.html) pages dogfood this exact mechanism. See `/embed.html`.
|
||
|
||
## 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 |
|
||
| `↑` / `↓` / `Home` / `End` | move the **cue** cursor (crosses set lists) |
|
||
| `PgUp` / `PgDn` | cue the previous / next set list |
|
||
| `Enter` | commit the cued item — switches on the next **bar** (smooth) |
|
||
| `Shift`+`Enter` | commit now — switches on the next **beat** (rude) |
|
||
| `N` / `P` | load next / previous immediately (rude quick‑step) |
|
||
| `Alt`+`↑` / `Alt`+`↓` | reorder the cued item |
|
||
| `1`–`9` | enable / silence lane 1–9 |
|
||
| `?` | shortcuts help |
|
||
| `Esc` | close the help / share dialog · cancel an armed switch |
|
||
|
||
(Arrow / navigation keys are left alone while a slider or dropdown is focused, so they still adjust it.)
|
||
|
||
## Live performance
|
||
|
||
The set list is performance-ready: you can line up where you're going next without
|
||
disturbing what's playing, then commit on a musical boundary — no audible gap.
|
||
|
||
- **Cue, then commit.** The arrows / `Home` / `End` / `PgUp` / `PgDn` move a *cue
|
||
cursor* (amber outline) through items — across set lists, without loading anything.
|
||
**`Enter`** commits the cued item with a **smooth** cutover at the next **bar**;
|
||
**`Shift`+`Enter`** is a **rude** cutover at the next **beat** ("wrong thing playing,
|
||
fix it now"). `N` / `P` are immediate rude quick‑steps. `Esc` cancels an armed switch.
|
||
- **Bar‑length segments.** Give an item a **bar** count (Timers box, or the `b<n>`
|
||
patch token) and a bar countdown (▦) shows bars remaining. With **Continue** on, it
|
||
auto‑advances to the next item at the bar boundary — so a *song* is just a set list
|
||
of segments (each with its own tempo, ramp and bar length) that hand off seamlessly.
|
||
- All transitions — manual or auto, beat or bar — keep the clock continuous; the loaded
|
||
item can even live in a set list you're not currently viewing (the player names it).
|
||
|
||
## Build
|
||
|
||
Every page is a source that shares code through `@BUILD:*` markers, so they all stay
|
||
in sync:
|
||
|
||
- `/*@BUILD:include:src/…@*/` inlines a **shared partial** — the audio/scheduler
|
||
engine (`src/engine.js`), the seed set lists (`src/setlists.js`, so every page ships
|
||
the **same default set lists** as the editor), and base styling (`src/base.css`:
|
||
reset + brand palette + type + the shared site header / nav / BOM table styles).
|
||
- `@BUILD:favicon@`, `@BUILD:logo-*@`, `/*@BUILD:samples@*/{}` inline the base64
|
||
assets from `assets/`.
|
||
|
||
`./build.sh` resolves every marker into a self‑contained page in `dist/` — the editor +
|
||
concepts + device mockups + the `info-*.html` pages — and copies `embed.js` through as‑is
|
||
(the editor inlines the CC0 samples; the device pages pass an empty `SAMPLES` for pure synth).
|
||
`dist/` is generated, git‑ignored — don't edit it by hand. `deploy.sh` runs the build first,
|
||
so a deploy always serves freshly assembled pages.
|
||
|
||
## Versioning
|
||
|
||
`VERSION` holds the formal version. `deploy.sh` builds, then 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.
|
||
|
||
## Files
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `index.html` | the **Concepts** landing / gallery (embeds each widget live) |
|
||
| `editor.html` | the **PE‑1 editor** app (source, with `@BUILD:*` markers) |
|
||
| `src/header.html` · `src/footer.html` · `src/chrome.js` | shared header / footer / theme chrome, inlined into every page |
|
||
| `player.html` · `teacher.html` · `stage.html` · `micro.html` · `showcase.html` | the device mockups (PM‑1 Initial / Teacher / Stage, PM‑µ Micro, PM‑S Showcase) |
|
||
| `info-*.html` | per‑form‑factor info pages (purpose + priced BOM for buildable hardware) |
|
||
| `embed.html` · `embed.js` | embed docs and the drop‑in widget loader |
|
||
| `src/` | shared partials inlined into every page: `engine.js`, `setlists.js`, `base.css` |
|
||
| `assets/` | base64 blobs inlined at build (samples, logos, favicon) |
|
||
| `build.sh` | resolve markers → self‑contained `dist/` pages |
|
||
| `deploy.sh` | build, then 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 public
|
||
repository is **<https://codeberg.org/VARASYS/metronome>** (also linked from the
|
||
in‑app **?** help).
|
||
|
||
### Credits
|
||
|
||
Acoustic drum one‑shots are from the **[Versilian Community Sample Library
|
||
(VCSL)](https://github.com/sgossner/VCSL)**, released under **CC0** (public
|
||
domain) — trimmed and downsampled, embedded inline. The 808/909 voices and the
|
||
electronic/percussion sounds are synthesized in Web Audio (no samples).
|