Phase 0: refresh README to current state; add 'kit' embed variant

README was stale (pre-dating several shipped changes). Rewrite it to match reality:
  - voices are all synthesized now (KIT_ALIAS -> 808/909); drop the VCSL samples
    narrative, the @BUILD:samples wording, and the sample credits.
  - document the lean-widget + separate info-<device>.html page model (was 'Open=Info').
  - add kit.html + all info-*.html to the Pages table; add the new src/ partials
    (header/footer/chrome/progbox/infoembed), pico/ firmware, and pico-main.py to Files/Build.
  - document the share-grammar additions: GM note numbers, Euclidean (k,n[,rot]), per-lane @<db> gain.
  - add a 'Build it (hardware)' section for the PM_K-1 Kit + MicroPython firmware.

embed.js: add the 'kit' form factor to the variant map (it previously fell back to micro),
so data-varasys-metronome="kit" embeds kit.html as the README now documents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-28 21:01:40 -05:00
parent 1afa56b078
commit 5fc962f0c4
2 changed files with 124 additions and 108 deletions

228
README.md
View file

@ -5,64 +5,76 @@ A landing page is the front door; the main app is the **PM_E1 PolyMeter Edito
web app where you stack as many "meter lanes" as you like, each its own little metronome 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 perstep pattern with accents. Layering lanes with a grouping, subdivision, drum voice and a perstep pattern with accents. Layering lanes
produces polymeter and true ratio polyrhythm. The same engine drives an everexpanding library produces polymeter and true ratio polyrhythm. The same engine drives an everexpanding library
of **formfactor concepts** (idealized and buildable hardware mockups) and ships as an of **formfactor concepts** (idealized and buildable hardware mockups), ships as an
**embeddable widget** anyone can drop into their own page. **embeddable widget** anyone can drop into their own page, and even runs as **firmware** on a
real Raspberry Pi Pico build (the **PM_K1 Kit**).
**Live:** https://metronome.varasys.io · **Source:** https://codeberg.org/VARASYS/metronome **Live:** https://metronome.varasys.io · **Source:** https://codeberg.org/VARASYS/metronome
Every **deployed page is a single, selfcontained `.html` file****zero dependencies**: Every **deployed page is a single, selfcontained `.html` file****zero dependencies**:
no framework, no CDN libraries, nothing fetched at runtime. They're assembled by a small 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 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. the brand assets (kept in `assets/`) into each page, so the sources stay lean. Every voice is
State (set lists, the practice log, theme and UI preferences) lives in `localStorage`. **synthesized** in Web Audio — there are no audio samples to load. State (set lists, the
practice log, theme and UI preferences) lives in `localStorage`.
## Pages ## Pages
The site is **one editor + a gallery of form factors**, and each form factor is split into a
**lean widget page** and a **separate info page**:
- **`<device>.html`** — just the live widget (front view, controls, program box). This is what
`?embed=1` serves and what the landing embeds; it never ships the BOM/narrative.
- **`info-<device>.html`** — the spec page: it embeds the live widget at the top, then the
description, dimensioned drawings and a priced **Bill of Materials** (for the buildable hardware).
| URL | What | | URL | What |
|-----|------| |-----|------|
| [`/`](https://metronome.varasys.io/) `index.html` | **Concepts** — the landing / formfactor gallery; each box embeds the live widget | | [`/`](https://metronome.varasys.io/) `index.html` | **Concepts** — the landing / formfactor gallery; each box embeds the live widget (Open ↗ / Specs & info ⓘ) |
| `/editor.html` | **PM_E1 — PolyMeter Editor** (the main app) | | `/editor.html` · `/info-editor.html` | **PM_E1 — PolyMeter Editor** (the main app) + its overview |
| `/player.html` | **PM_C1 Concept** — idealized concept device (full display + setlist nav, theme, fullscreen "stage" view) | | `/kit.html` · `/info-kit.html` | **PM_K1 Kit** — buildable Raspberry Pi Pico touchscreen unit (52Pi EP0172); info page has the wiring, parts and firmware |
| `/teacher.html` | **PM_T1 Teacher** — studio / lesson console (colour TFT, arcade buttons, 1/4″ instrument passthrough with analog click injection) | | `/player.html` · `/info-player.html` | **PM_C1 Concept** — idealized concept device (full display + setlist nav, theme, fullscreen "stage" view) |
| `/stage.html` | **PM_S1 Stage** — footpedal stompbox (two footswitches, expressionpedal in, RGB beat light, instrument passthrough) | | `/teacher.html` · `/info-teacher.html` | **PM_T1 Teacher** — studio / lesson console (colour TFT, arcade buttons, 1/4″ instrument passthrough with analog click injection) |
| `/micro.html` | **PM_P1 Practice** — inline practice bar (instrument in / out passthrough, clickable thumbroller, 14segment display) | | `/stage.html` · `/info-stage.html` | **PM_S1 Stage** — footpedal stompbox (two footswitches, expressionpedal in, RGB beat light, instrument passthrough) |
| `/showcase.html` | **PM_D1 Display** — pyramid display piece; the pendulum is an RGB light bar combining every lane's subdivisions/accents | | `/micro.html` · `/info-micro.html` | **PM_P1 Practice** — inline practice bar (instrument in / out passthrough, clickable thumbroller, 14segment display) |
| `/showcase.html` · `/info-showcase.html` | **PM_D1 Display** — pyramid display piece; the pendulum is an RGB light bar combining every lane's subdivisions/accents |
| `/embed.html` · `/embed.js` | embed docs and the dropin loader | | `/embed.html` · `/embed.js` | embed docs and the dropin loader |
| `/pico-main.py` | the PM_K1 MicroPython firmware (download) |
Each formfactor page is selfcontained ("Open" = "Info"): a moredetailed description, The buildable units (Teacher, Stage, Practice, Display, Kit) carry a priced BOM on their info
the live device with front + top/side dimensioned views and loading instructions, and an page; the Editor (web app) and Concept have none. Every page shares the same VARASYS header
expandable **Spec & BOM** (priced, for the buildable hardware). The buildable units are the (official logo with bakedin tagline, nav, theme toggle). The editor also shows a subtle live
Teacher, Stage, Micro and Showcase; the Editor (web app) and Initial (concept) have no BOM. **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)).
Each page carries the same VARASYS header (logo + tagline, nav, theme toggle). The editor Because nothing loads from the network, you can save a page (`Ctrl`/`⌘`+`S`) and open it
also shows a subtle live **program string** of what's loaded — editable, with copy/paste — straight from disk to run fully offline. One catch from a local `file://`: the browser may not
under the app (press `Enter` or paste to apply; see [the share language](#the-share-language)). persist `localStorage` between sessions, so use **Export all** (setlist **⋯** menu) to back up.
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 ## Features
- **Meter lanes** — grouping (odd meters), subdivision (incl. swing), a drum/percussion - **Meter lanes** — grouping (odd meters), subdivision (incl. swing), a drum/percussion
voice, per**step dynamics** (accent / normal / ghost / mute), mute, live measure counter. voice, per**step dynamics** (accent / normal / ghost / mute), mute, live measure counter.
- **Sounds** — a sampled acoustic kit plus synthesized **808 / 909** and electronic voices; - **Sounds** — every voice is **synthesized** in Web Audio: a friendly drum kit
click each pad to set its dynamics; pick a *swing* subdivision for a triplet feel. (`kick`, `snare`, `hatClosed`, …) rendered with the **808 / 909** voices, the 808/909 voices
by name, and electronic/percussion tones. No samples are loaded.
- **Perlane gain** — a dB trim knob per lane (`@<db>` in the share language), applied at
schedule time so changing it never stutters playback.
- **Polyrhythm** — a perlane *poly* toggle fits a lane's beats evenly into lane 1's - **Polyrhythm** — a perlane *poly* toggle fits a lane's beats evenly into lane 1's
bar (e.g. 5over4, 3over2). bar (e.g. 5over4, 3over2).
- **Euclidean rhythms** — spread *k* hits evenly across *n* steps with `(k,n[,rot])`.
- **Practice** — gap/mute trainer (play N / mute M bars) and a tempo ramp with a - **Practice** — gap/mute trainer (play N / mute M bars) and a tempo ramp with a
start BPM and signed step. start BPM and signed step.
- **Set lists** — named, ordered lists of saved setups; **cue** across lists and commit - **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 on a bar/beat boundary with no audible gap (see **Live performance**); each play is logged.
logged for crossday comparison.
- **Sharing** — copy a link to your current settings or a whole set list. - **Sharing** — copy a link to your current settings or a whole set list.
- **Theming** — System / Light / Dark. - **Theming** — System / Light / Dark.
## The share language ## The share language
A compact, humanreadable text encodes a full configuration (a *patch*). It's what 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. goes in a share link, the editor's program box, and a device's program list. You can
handwrite or edit it.
### Patch grammar ### Patch grammar
@ -86,29 +98,26 @@ Tokens are joined with `;`. `tr` and `rmp` are omitted when off.
### Lane grammar ### Lane grammar
``` ```
<sound> : <grouping> [ / <sub> ] [ = <pattern> ] [ ~ ] [ ! ] <sound> : <grouping> [ / <sub> ] [ (<k>,<n>[,<rot>]) ] [ = <pattern> ] [ @ <db> ] [ ~ ] [ ! ]
``` ```
- **sound** — the acoustic kit: `beep`, `kick`, `snare`, `rim`, `clap`, `hatClosed`, - **sound** — a synthesized voice. Friendly kit names (rendered with 808/909):
`hatOpen`, `ride`, `crash`, `tomLow`, `tomMid`, `tomHigh`, `tambourine`, `cowbell`, `beep`, `kick`, `snare`, `rim`, `clap`, `hatClosed`, `hatOpen`, `ride`, `crash`, `tomLow`,
`woodblock`, `claves`, `jamblock` (kick, snare, closedhat, crash, toms, tambourine, `tomMid`, `tomHigh`, `tambourine`, `cowbell`, `woodblock`, `claves`; the drummachine voices
cowbell, woodblock and claves play embedded CC0 samples; `beep`, `clap`, `rim`, by name — `kick808 snare808 clap808 hat808 openHat808 cowbell808 tom808` and
`hatOpen` and `ride` stay synthesized — VCSL has no clean source for those); plus `kick909 snare909 clap909 hat909 ride909 crash909`; or a **GeneralMIDI note number**
synthesized drum machines — (`36`→kick, `38`→snare, `42`→closed hat, …). Unknown → `beep`.
`kick808 snare808 clap808 hat808 openHat808 cowbell808 tom808` and - **grouping** — beats per bar, optionally grouped for odd meters: `4`, `3`, `2+2+3`.
`kick909 snare909 clap909 hat909 ride909 crash909`. Unknown → `beep`. Groups get a visual divider; accents are perstep (see `=pattern`).
- **grouping** — beats per bar, optionally grouped for odd meters: `4`, `3`, - **`/sub`** — subdivision: `1` quarter (default), `2` eighth, `3` triplet, `4` sixteenth,
`2+2+3`. Groups get a visual divider; accents are perstep (see `=pattern`). `6` sextuplet — also sets how many **pads** each beat splits into. Append **`s`** for **swing**
- **`/sub`** — subdivision: `1` quarter (default), `2` eighth, `3` triplet, on even subdivisions (`2s`, `4s`) to delay the offbeats to a 2:1 triplet feel.
`4` sixteenth, `6` sextuplet. This also sets how many **pads** each beat splits - **`(k,n[,rot])`** — **Euclidean** fill: place `k` hits as evenly as possible across `n`
into. Append **`s`** for **swing** on even subdivisions — `2s` (swung eighths) or steps, optionally rotated by `rot`. e.g. `kick:4(3,8)`.
`4s` (swung sixteenths) delay the offbeats to a triplet (2:1) feel. Omit for quarter. - **`=pattern`** — per**step dynamics**, one char per pad: **`X`** accent, **`x`** normal,
- **`=pattern`** — per**step dynamics**, one char per pad: **`X`** accent, **`x`** **`g`** ghost (soft), **`.`** `-` `_` mute (rest). Length = beats × `sub`. Omit to get the
normal, **`g`** ghost (soft), **`.`** mute (rest). Length = beats per bar × `sub`. Omit to get the default — first step of each beat accented, the rest normal. e.g. `4=.X.X` accents 2 & 4.
default — the first step of **each beat** accented, the rest normal (click a pad in - **`@<db>`** — perlane gain trim in decibels, e.g. `@-3` or `@+2`.
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 beatcount patterns still parse.)
- **`~`** — polyrhythm: fit this lane's beats evenly into **lane 1's** bar. - **`~`** — polyrhythm: fit this lane's beats evenly into **lane 1's** bar.
- **`!`** — mute the lane. - **`!`** — mute the lane.
@ -120,28 +129,27 @@ Tokens are joined with `;`. `tr` and `rmp` are omitted when off.
| `snare:4=.X.X` | accented snare backbeat (2 & 4) | | `snare:4=.X.X` | accented snare backbeat (2 & 4) |
| `hatClosed:4/2` | eighthnote hihats (downbeat of each beat accented) | | `hatClosed:4/2` | eighthnote hihats (downbeat of each beat accented) |
| `ride:4/2s` | **swung** eighthnote ride | | `ride:4/2s` | **swung** eighthnote ride |
| `kick:4(3,8)` | a 3over8 Euclidean kick |
| `claves:5~` | 5 evenly across lane 1's bar (5over4 if lane 1 is `4`) | | `claves:5~` | 5 evenly across lane 1's bar (5over4 if lane 1 is `4`) |
| `hat909:4/2@-4` | eighth 909 hats, trimmed 4 dB |
| `kick:2+2+3=x..x..x` | 7/8, kick on each group start | | `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 | | **Full:** `v1;t120;kick:4;snare:4=.x.x;hatClosed:4/2;tr2/2` | backbeat groove with gap trainer |
### In URLs ### In URLs
- **Settings:** `…/#p=<patch>` — readable, e.g. - **Settings:** `…/#p=<patch>` — readable, e.g. `…/#p=v1;t120;kick:4;claves:5~`
`…/#p=v1;t120;kick:4;claves:5~` - **Set list:** `…/#sl=<base64url>` — a JSON `{title, description, items[]}` where each
- **Set list:** `…/#sl=<base64url>` — a JSON `{title, description, items[]}` where item's config is a patch string.
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 Opening such a link applies the settings (or imports the set list) on load, then clears
clears the hash so a refresh won't reimport. the hash so a refresh won't reimport.
## Sharing ## Sharing
In the setlist panel's **⋯** menu: In the setlist panel's **⋯** menu:
- **Share settings link** / **Share setlist link** open a dialog with the link to - **Share settings link** / **Share setlist link** open a dialog with the link to **Copy**
**Copy** or **Open**. The link encodes everything in the URL — nothing is uploaded. 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 - **Export all / Import file** back up your set lists and practice log as a JSON file.
file (a legacy `presets` field is included for backward compatibility).
## Embedding ## Embedding
@ -155,16 +163,30 @@ container and the loader script — it builds an `<iframe>` to the chromestri
<script src="https://metronome.varasys.io/embed.js"></script> <script src="https://metronome.varasys.io/embed.js"></script>
``` ```
- `data-varasys-metronome` — variant: `editor` · `initial` · `teacher` · `stage` · `micro` · `showcase`. - `data-varasys-metronome` — variant: `editor` · `kit` · `initial` · `teacher` · `stage` · `micro` · `showcase`.
- `data-patch` — a [patch string](#patch-grammar) (maps to `#p=`); or `data-setlist` - `data-patch` — a [patch string](#patch-grammar) (maps to `#p=`); or `data-setlist`
for a setlist code (maps to `#sl=`). for a setlist code (maps to `#sl=`).
- `data-width` / `data-height` — optional initial size (default `100%` × `300px`; - `data-width` / `data-height` — optional initial size (default `100%` × `300px`;
height then tracks the widget, which posts `{type:'varasys-h', h}` to the parent). 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. The Prefer your own iframe? `…/<variant>.html?embed=1#p=<patch>` works directly. The
[Concepts landing](index.html) dogfoods this exact mechanism (every box is a live widget). [Concepts landing](index.html) and every `info-*.html` page dogfood this exact mechanism.
See `/embed.html`. See `/embed.html`.
## Build it (hardware) — PM_K1 "Kit"
The **PM_K1 Kit** runs the same engine and program strings on a real device you can build today:
a **Raspberry Pi Pico** on the **52Pi EP0172 "Pico Breadboard Kit Plus"** — a 3.5″ ST7796
320×480 capacitivetouch screen (GT911), a PSP joystick, a WS2812 RGB LED, a buzzer and two
buttons, all prewired. See **`/info-kit.html`** for the pinout, parts (~$45 incl. Pico) and
flashing steps. Firmware lives in **`pico/`**:
- **`pico/main.py`** — singlefile **MicroPython** firmware: an ST7796 driver, GT911 touch,
WS2812 RGB, PWM buzzer, ADC joystick, baked antialiased fonts, and the polymeter engine.
It parses the same program strings as the web editor. Flash MicroPython, copy `main.py`,
edit the `PROGRAMS` list to change grooves. Download: `/pico-main.py`.
- **`pico/gen_font.py`** — generates the baked antialiased fonts embedded in the firmware.
## Keyboard shortcuts ## Keyboard shortcuts
| Key | Action | | Key | Action |
@ -190,35 +212,33 @@ See `/embed.html`.
The set list is performance-ready: you can line up where you're going next without 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. 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 - **Cue, then commit.** The arrows / `Home` / `End` / `PgUp` / `PgDn` move a *cue cursor*
cursor* (amber outline) through items — across set lists, without loading anything. (amber outline) through items — across set lists, without loading anything. **`Enter`**
**`Enter`** commits the cued item with a **smooth** cutover at the next **bar**; commits with a **smooth** cutover at the next **bar**; **`Shift`+`Enter`** is a **rude**
**`Shift`+`Enter`** is a **rude** cutover at the next **beat** ("wrong thing playing, cutover at the next **beat**. `N` / `P` are immediate rude quicksteps. `Esc` cancels.
fix it now"). `N` / `P` are immediate rude quicksteps. `Esc` cancels an armed switch. - **Barlength segments.** Give an item a **bar** count (Timers box, or `b<n>`) and a bar
- **Barlength segments.** Give an item a **bar** count (Timers box, or the `b<n>` countdown (▦) shows bars remaining. With **Continue** on, it autoadvances at the bar
patch token) and a bar countdown (▦) shows bars remaining. With **Continue** on, it boundary — so a *song* is just a set list of segments that hand off seamlessly.
autoadvances to the next item at the bar boundary — so a *song* is just a set list - All transitions keep the clock continuous; the loaded item can live in a set list you're
of segments (each with its own tempo, ramp and bar length) that hand off seamlessly. not currently viewing (the player names it).
- 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 ## Build
Every page is a source that shares code through `@BUILD:*` markers, so they all stay Every page is a source that shares code through `@BUILD:*` markers, so they all stay in sync:
in sync:
- `/*@BUILD:include:src/…@*/` inlines a **shared partial** — the audio/scheduler - `/*@BUILD:include:src/…@*/` inlines a **shared partial** — the audio/scheduler engine
engine (`src/engine.js`), the seed set lists (`src/setlists.js`, so every page ships (`src/engine.js`), the seed set lists (`src/setlists.js`, so every page ships the **same
the **same default set lists** as the editor), and base styling (`src/base.css`: default set lists**), base styling (`src/base.css`), the site **header/footer/chrome**
reset + brand palette + type + the shared site header / nav / BOM table styles). (`src/header.html`, `src/footer.html`, `src/chrome.js`), the perdevice **program box**
- `@BUILD:favicon@`, `@BUILD:logo-*@`, `/*@BUILD:samples@*/{}` inline the base64 (`src/progbox.{html,js}`) and the infopage **livewidget embed** (`src/infoembed.{html,js}`).
assets from `assets/`. - `@BUILD:favicon@`, `@BUILD:logo-dark@`, `@BUILD:logo-light@` inline the base64 assets from
`assets/` (the official logos already include the tagline).
`./build.sh` resolves every marker into a selfcontained page in `dist/` — the editor + `./build.sh` resolves every marker into a selfcontained page in `dist/` (the Concepts landing,
the Concepts landing + editor + device/form-factor pages — and copies `embed.js` through asis the editor, the device/formfactor pages and their `info-*.html`), copies `embed.js` through
(the editor inlines the CC0 samples; the device pages pass an empty `SAMPLES` for pure synth). asis, and copies the Pico firmware to `dist/pico-main.py`. `dist/` is generated, gitignored —
`dist/` is generated, gitignored — don't edit it by hand. `deploy.sh` runs the build first, don't edit it by hand. `deploy.sh` runs the build first, so a deploy always serves freshly
so a deploy always serves freshly assembled pages. assembled pages.
## Versioning ## Versioning
@ -227,9 +247,8 @@ so a deploy always serves freshly assembled pages.
- **Formal** — a clean commit tagged `v<VERSION>``X.Y.Z`. - **Formal** — a clean commit tagged `v<VERSION>``X.Y.Z`.
- **Dev** — anything else → `X.Y.Z-dev.<utc-timestamp>.<short-sha>[.dirty]`. - **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 Cut a release with `./release.sh [X.Y.Z]` — the optional arg bumps & commits `VERSION`; it then
`VERSION`; it then tags the current commit `v<VERSION>` (requires a clean tree). tags the current commit `v<VERSION>` (requires a clean tree). Push the tag, then deploy.
Push the tag, then deploy.
## Files ## Files
@ -237,12 +256,13 @@ Push the tag, then deploy.
|------|---------| |------|---------|
| `index.html` | the **Concepts** landing / gallery (embeds each widget live) | | `index.html` | the **Concepts** landing / gallery (embeds each widget live) |
| `editor.html` | the **PM_E1 editor** app (source, with `@BUILD:*` markers) | | `editor.html` | the **PM_E1 editor** app (source, with `@BUILD:*` markers) |
| `src/header.html` · `src/footer.html` · `src/chrome.js` | shared header / footer / theme chrome, inlined into every page | | `kit.html` · `player.html` · `teacher.html` · `stage.html` · `micro.html` · `showcase.html` | the device widget pages (PM_K1 Kit, PM_C1 Concept, Teacher, Stage, PM_P1 Practice, PM_D1 Display) |
| `player.html` · `teacher.html` · `stage.html` · `micro.html` · `showcase.html` | the device mockups (PM_C1 Concept / Teacher / Stage, PM_P1 Practice, PM_D1 Display) | | `info-*.html` | performfactor spec pages (embed the live widget + description + dimensions + BOM) |
| `embed.html` · `embed.js` | embed docs and the dropin widget loader | | `embed.html` · `embed.js` | embed docs and the dropin widget loader |
| `src/` | shared partials inlined into every page: `engine.js`, `setlists.js`, `base.css` | | `src/` | shared partials inlined into every page: `engine.js`, `setlists.js`, `base.css`, `header.html`, `footer.html`, `chrome.js`, `progbox.{html,js}`, `infoembed.{html,js}` |
| `assets/` | base64 blobs inlined at build (samples, logos, favicon) | | `assets/` | base64 blobs inlined at build (`favicon`, `logo-dark`, `logo-light`) |
| `build.sh` | resolve markers → selfcontained `dist/` pages | | `pico/` | PM_K1 firmware: `main.py` (MicroPython), `gen_font.py` (font generator), `README.md` |
| `build.sh` | resolve markers → selfcontained `dist/` pages (+ `pico-main.py`) |
| `deploy.sh` | build, then publish to the Caddy web root | | `deploy.sh` | build, then publish to the Caddy web root |
| `release.sh` | tag a formal version | | `release.sh` | tag a formal version |
| `VERSION` | formal version string | | `VERSION` | formal version string |
@ -252,19 +272,15 @@ Push the tag, then deploy.
Copyright (C) 2026 Varasys. Copyright (C) 2026 Varasys.
This program is free software: you can redistribute it and/or modify it under This program is free software: you can redistribute it and/or modify it under the terms of the
the terms of the **GNU Affero General Public License** as published by the Free **GNU Affero General Public License** as published by the Free Software Foundation, either
Software Foundation, either version 3 of the License, or (at your option) any version 3 of the License, or (at your option) any later version. See [`LICENSE`](LICENSE).
later version. See [`LICENSE`](LICENSE) for the full text.
Because the app is served over a network, the AGPL's §13 applies: anyone Because the app is served over a network, the AGPL's §13 applies: anyone interacting with a
interacting with a hosted instance must be able to get its source — the public hosted instance must be able to get its source — the public repository is
repository is **<https://codeberg.org/VARASYS/metronome>** (also linked from the **<https://codeberg.org/VARASYS/metronome>** (also linked from the inapp **?** help).
inapp **?** help).
### Credits ### Credits
Acoustic drum oneshots are from the **[Versilian Community Sample Library All drum and percussion voices are **synthesized in Web Audio** (808/909style and electronic) —
(VCSL)](https://github.com/sgossner/VCSL)**, released under **CC0** (public there are no audio samples. The ondevice fonts (PM_K1) are rendered from **DejaVu Sans**.
domain) — trimmed and downsampled, embedded inline. The 808/909 voices and the
electronic/percussion sounds are synthesized in Web Audio (no samples).

View file

@ -5,7 +5,7 @@
* <script src="https://metronome.varasys.io/embed.js"></script> * <script src="https://metronome.varasys.io/embed.js"></script>
* *
* Attributes: * Attributes:
* data-varasys-metronome editor | initial | teacher | stage | micro | showcase (which form factor) * data-varasys-metronome editor | kit | initial | teacher | stage | micro | showcase (which form factor)
* data-patch a PolyMeter program/settings string (preloads it) * data-patch a PolyMeter program/settings string (preloads it)
* data-setlist a base64url set-list code (alternative to data-patch) * data-setlist a base64url set-list code (alternative to data-patch)
* data-width / data-height iframe size (default 100% × 300; height auto-grows) * data-width / data-height iframe size (default 100% × 300; height auto-grows)
@ -14,7 +14,7 @@
* own ?embed=1 mode strips the site chrome) and auto-resizes to the widget's content. * own ?embed=1 mode strips the site chrome) and auto-resizes to the widget's content.
*/ */
(function () { (function () {
var PAGES = { editor: "editor.html", initial: "player.html", teacher: "teacher.html", var PAGES = { editor: "editor.html", kit: "kit.html", initial: "player.html", teacher: "teacher.html",
stage: "stage.html", micro: "micro.html", showcase: "showcase.html" }; stage: "stage.html", micro: "micro.html", showcase: "showcase.html" };
var me = document.currentScript; var me = document.currentScript;
var ORIGIN = me ? me.src.replace(/\/embed\.js(\?.*)?$/, "") : location.origin; var ORIGIN = me ? me.src.replace(/\/embed\.js(\?.*)?$/, "") : location.origin;