No description
Find a file
Me Here 15755f4d0c pm-kit: hidden stepper jog/test mode (hold A+B at boot)
Hold both buttons at power-on to enter a self-contained jog screen: the joystick
spins the stepper CW/CCW (speed by deflection), with an on-screen direction
needle + RGB LED feedback. Runs in its own loop; power-cycle to return to normal.

- app.py: _jog_loop drawn entirely in the overlay group (cover + labels + needle);
  Pendulum.spin() does a free half-step either way; _jog set when A+B held in init;
  run() branches to it before the normal loop.
- boot.py: editor mode is now "A alone" (A pressed, B not). A+B stays in appliance
  mode, so the jog chord doesn't also flip the drive writable.

Pure ASCII; conformance 47/47; build precompiles app.mpy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 21:37:57 -05:00
assets Add untracked notation deliverables (build/compile depend on these) 2026-06-02 13:46:45 -05:00
docs pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
hardware DESIGN: route LCD TE pin on production face for tear-free updates 2026-06-01 13:04:42 -05:00
pico Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
pico-cp pm-kit: hidden stepper jog/test mode (hold A+B at boot) 2026-06-05 21:37:57 -05:00
pico-explorer Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
pico-scroll Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
pico-wm8960 PM_K-1 Phase 1: bench prototype firmware + doc (Pico 2 + WM8960, transformer-isolated XLR) 2026-06-01 09:15:26 -05:00
rust pm-daisy: Daisy Pod spike — play the click engine on STM32H7 (host-verified, awaiting hardware) 2026-06-05 11:41:10 -05:00
src midi-out: 24-PPQN clock + Start/Stop; shared src/midiout.js; mirror into editor.html 2026-06-04 14:11:24 -05:00
tests Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
wokwi Implement per-track playback flow (rep / end / relative goto) 2026-05-31 00:37:06 -05:00
.gitignore Implement per-track playback flow (rep / end / relative goto) 2026-05-31 00:37:06 -05:00
build.sh pm-grid: USB-MIDI audio + make Rust the shipping Grid firmware 2026-06-03 15:19:38 -05:00
CLAUDE.md Add PM_G-1 "Grid" form factor (Pimoroni Pico Scroll Pack) + Rust core/driver plan 2026-05-31 20:30:15 -05:00
deploy.sh pm-grid: USB-MIDI audio + make Rust the shipping Grid firmware 2026-06-03 15:19:38 -05:00
editor-beta.html editor: move VARASYS logo into the device top-left (out of the site header) 2026-06-02 08:25:59 -05:00
editor.html midi-out: 24-PPQN clock + Start/Stop; shared src/midiout.js; mirror into editor.html 2026-06-04 14:11:24 -05:00
embed.html Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
embed.js Add PM_G-1 "Grid" form factor (Pimoroni Pico Scroll Pack) + Rust core/driver plan 2026-05-31 20:30:15 -05:00
explorer.html Landing page: explorer.html widget mockup 2026-05-30 20:55:20 -05:00
grid.html Add PM_G-1 "Grid" form factor (Pimoroni Pico Scroll Pack) + Rust core/driver plan 2026-05-31 20:30:15 -05:00
index.html web: hide PM_E-1 from landing (focus PM_E-2); clarify pm_e-2 device controls 2026-06-04 12:23:15 -05:00
info-editor.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
info-explorer.html Landing page: add PM_X-1 Explorer pane 2026-05-30 20:49:55 -05:00
info-grid.html pm-grid: USB-MIDI audio + make Rust the shipping Grid firmware 2026-06-03 15:19:38 -05:00
info-kit.html PM_K-1 0.0.14: gapless seam + continuous ramp + MIDI Clock Out (master); speaker rename 2026-05-30 07:11:19 -05:00
info-micro.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
info-player.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
info-pm_e-2.html Add untracked notation deliverables (build/compile depend on these) 2026-06-02 13:46:45 -05:00
info-showcase.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
info-stage.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
info-teacher.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
kit.html PM_K-1: drop the VARASYS wordmark from the screen UI (it's on the case) 2026-05-28 16:19:08 -05:00
LICENSE Add GNU AGPL v3 license 2026-05-25 08:33:40 -05:00
micro.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
player.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
pm_e-2.html midi-out: 24-PPQN clock + Start/Stop; shared src/midiout.js; mirror into editor.html 2026-06-04 14:11:24 -05:00
README.md Preserve notation + grammar feature work (verified complete + green) 2026-06-02 13:45:26 -05:00
release.sh Add versioning: VERSION file, dev/formal stamping, release.sh 2026-05-24 16:59:31 -05:00
showcase.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
stage.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
teacher.html Split each form factor into a lean widget page + a separate info page 2026-05-28 14:18:23 -05:00
VERSION Bump version to 0.0.68 2026-05-29 06:55:58 -05:00

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 PM_E1 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 perstep pattern with accents. Layering lanes produces polymeter and true ratio polyrhythm. The same engine drives an everexpanding library of formfactor concepts (idealized and buildable hardware mockups), ships as an 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

Every deployed page is a single, selfcontained .html filezero 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 brand assets (kept in assets/) into each page, so the sources stay lean. Every voice is 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

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
/ index.html Concepts — the landing / formfactor gallery; each box embeds the live widget (Open ↗ / Specs & info ⓘ)
/editor.html · /info-editor.html PM_E1 — PolyMeter Editor (the main app) + its overview
/pm_e-2.html · /info-pm_e-2.html PM_E2 — PolyMeter Editor (Notation) — second-gen, engraved drum notation (Bravura/SMuFL): Staff / TUBS / Konnakol views, edit-on-staff
/kit.html · /info-kit.html PM_K1 Kit — buildable Raspberry Pi Pico touchscreen unit (52Pi EP0172); info page has the wiring, parts and firmware
/player.html · /info-player.html PM_C1 Concept — idealized concept device (full display + setlist nav, theme, fullscreen "stage" view)
/teacher.html · /info-teacher.html PM_T1 Teacher — studio / lesson console (colour TFT, arcade buttons, 1/4″ instrument passthrough with analog click injection)
/stage.html · /info-stage.html PM_S1 Stage — footpedal stompbox (two footswitches, expressionpedal in, RGB beat light, instrument passthrough)
/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
/pico-main.py the PM_K1 MicroPython firmware (download)

The buildable units (Teacher, Stage, Practice, Display, Kit) carry a priced BOM on their info page; the Editor (web app) and Concept have none. Every page shares the same VARASYS header (official logo with bakedin 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).

Because nothing loads from the network, you can save a 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.

Features

  • Meter lanes — grouping (odd meters), subdivision (incl. swing), a drum/percussion voice, perstep dynamics (accent / normal / ghost / mute), mute, live measure counter.
  • Sounds — every voice is synthesized in Web Audio: a friendly drum kit (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 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 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.
  • Sharing — copy a link 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, the editor's program box, and a device's program list. You can handwrite 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 0100 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> ] [ (<k>,<n>[,<rot>]) ] [ = <pattern> ] [ @ <db> ] [ ~ ] [ ! ]
  • sound — a synthesized voice. Friendly kit names (rendered with 808/909): beep, kick, snare, rim, clap, hatClosed, hatOpen, ride, crash, tomLow, tomMid, tomHigh, tambourine, cowbell, woodblock, claves; the drummachine voices by name — kick808 snare808 clap808 hat808 openHat808 cowbell808 tom808 and kick909 snare909 clap909 hat909 ride909 crash909; or a GeneralMIDI note number (36→kick, 38→snare, 42→closed hat, …). Unknown → beep.
  • grouping — beats per bar, optionally grouped for odd meters: 4, 3, 2+2+3. Groups get a visual divider; accents are perstep (see =pattern).
  • /sub — subdivision: 1 quarter (default), 2 eighth, 3 triplet, 4 sixteenth, 6 sextuplet — also sets how many pads each beat splits into. Append s for swing on even subdivisions (2s, 4s) to delay the offbeats to a 2:1 triplet feel.
  • (k,n[,rot])Euclidean fill: place k hits as evenly as possible across n steps, optionally rotated by rot. e.g. kick:4(3,8).
  • =pattern — perstep dynamics, one char per pad: X accent, x normal, g ghost (soft), . - _ mute (rest). Length = beats × sub. Omit to get the default — first step of each beat accented, the rest normal. e.g. 4=.X.X accents 2 & 4.
  • @<db> — perlane gain trim in decibels, e.g. @-3 or @+2.
  • ~ — 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 eighthnote hihats (downbeat of each beat accented)
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)
hat909:4/2@-4 eighth 909 hats, trimmed 4 dB
kick:2+2+3=x..x..x 7/8, kick on each group start
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.

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. 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.

Embedding

Any form factor can be embedded in another page as a selfsizing widget. Drop in a container and the loader script — it builds an <iframe> to the chromestripped (?embed=1) page, preloads your config string, and autoresizes to the content:

<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 · kit · initial · teacher · stage · micro · showcase.
  • data-patch — a patch string (maps to #p=); or data-setlist for a setlist 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. The Concepts landing and every info-*.html page dogfood this exact mechanism. 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 (used by both firmwares).
  • pico-cp/ — a CircuitPython edition (download /pm_k1_circuitpy.zip): a selfcontained appliance. The Pico mounts as a USB drive carrying the firmware + your programs.json + an offline editor, drives a full lanes/pads touchscreen, logs practice to history.json on the device, takes set lists pushed from the editor over USBMIDI (with a universal downloadanddrag fallback), and plays out your computer's speakers over USBMIDI (the editor's 🎹 Device audio). By default the firmware owns the drive (readonly to the computer, so it's protected); hold button A at poweron for editor mode (drive writable). Firmware updates are one click from the editor (⋯ → Update firmware) — pushed over USBMIDI as an A/B update with automatic rollback. The MicroPython build stays the simple, nocomputer option.

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 quickstep)
Alt+ / Alt+ reorder the cued item
19 enable / silence lane 19
? 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 with a smooth cutover at the next bar; Shift+Enter is a rude cutover at the next beat. N / P are immediate rude quicksteps. Esc cancels.
  • Barlength segments. Give an item a bar count (Timers box, or b<n>) and a bar countdown (▦) shows bars remaining. With Continue on, it autoadvances at the bar boundary — so a song is just a set list of segments that hand off seamlessly.
  • All transitions keep the clock continuous; the loaded item can 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), base styling (src/base.css), the site header/footer/chrome (src/header.html, src/footer.html, src/chrome.js), the perdevice program box (src/progbox.{html,js}) and the infopage livewidget embed (src/infoembed.{html,js}).
  • @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 Concepts landing, the editor, the device/formfactor pages and their info-*.html), copies embed.js through asis, and copies the Pico firmware to dist/pico-main.py. dist/ is generated, gitignored — 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 PM_E1 editor app (source, with @BUILD:* markers)
pm_e-2.html · src/notation.js the PM_E2 notation editor + its Bravura/SMuFL engraving engine (tools/bravura/ subsets the font → assets/bravura.woff2.b64, inlined via @BUILD:bravura@)
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)
info-*.html performfactor spec pages (embed the live widget + description + dimensions + BOM)
embed.html · embed.js embed docs and the dropin widget loader
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 (favicon, logo-dark, logo-light)
pico/ PM_K1 MicroPython firmware: main.py, gen_font.py (font generator), README.md
pico-cp/ PM_K1 CircuitPython edition: code.py, programs.json, font_*.bin, README.md (bundled + served as /pm_k1_circuitpy.zip)
build.sh resolve markers → selfcontained dist/ pages (+ pico-main.py)
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.

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 inapp ? help).

Credits

All drum and percussion voices are synthesized in Web Audio (808/909style and electronic) — there are no audio samples. The ondevice fonts (PM_K1) are rendered from DejaVu Sans.