The editor's 'Device audio' now sends a MIDI Active-Sensing heartbeat (0xFE, every 250ms)
to the device while on. The firmware reads usb_midi.ports[0]; while it hears the heartbeat
(<1s) it shows a green 'MIDI' badge top-right and silences the buzzer (the computer plays);
~1s after it stops, it reverts to the buzzer and hides the badge. Manual MUTE_BUZZER still
works. Verified headless: host detected -> MIDI shown + buzzer duty 0; timeout -> reverts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The firmware ran every lane at a fixed beat/sub, ignoring the ~ flag, so a poly lane
(e.g. cowbell:3~) played quarter-notes instead of fitting its cycle into lane 1's bar —
the duple 'and' coincided with a triplet note. Now match the web engine: a poly lane's
whole cycle spans the master lane's bar (dur = master_bar / steps). Verified: claves:5~
over kick:4 -> both cycles = 2.400s (5-over-4); 3-over-2 lands correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
If the device isn't seen as a MIDI input (USB endpoint pressure from drive+serial+HID+MIDI),
boot.py disables the unused HID interface and enables usb_midi — copy it on and power-cycle.
Bundled into pm_k1_circuitpy.zip; documented in the README.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reported no computer audio + 'no device being controlled'. Add visibility to
diagnose: the button now shows the connected MIDI input's name (or 'no device'), the
toggle alert lists detected inputs, and the button pulses green on each Note-On
received — so it's clear whether the device is seen and whether notes are arriving.
Also call ensureAudio() in the message handler as a guard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Firmware (pico-cp/code.py): on every click, send a USB-MIDI note-on per firing lane —
GM drum note by voice (SOUND_GM), velocity by level (accent/normal/ghost) — via the
default-enabled usb_midi.ports[1]. Polyphonic, so the computer plays the full groove.
New CONFIG: MIDI_ENABLED (default on), MUTE_BUZZER (silence the buzzer when using
computer audio).
Editor (editor.html): a '🎹 Device audio' toggle uses the Web MIDI API
(requestMIDIAccess) to voice incoming notes through the existing synth — Note-On ->
GM_NUM[note] / velocity-to-gain -> playInstrument(). The device is the clock; the
browser is the sound module, locked in sync. Chrome/Edge.
Verified: firmware emits the right notes (kick+hat on beat 1 of four-on-the-floor,
snare's rest skipped); editor loads clean with the toggle + handlers present. Docs
(info-kit, both READMEs) updated. The on-device buzzer/screen still work standalone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add to the editor's set-list ⋯ menu:
- 📟 Save to device — writes the active set list as programs.json (the same file the
PM_K-1 firmware reads). Uses the File System Access API to write straight onto the
CIRCUITPY drive (Chrome/Edge); falls back to a download to drag on. Reuses
setupToPatch() per item -> {title, programs:[{name, prog}]}.
- 📥 Load from device — reads a programs.json back into a new set list (patchToSetup
per item; reuses the existing import path).
Bundle the built editor.html into pm_k1_circuitpy.zip so the drive carries its own
offline programmer. info-kit + pico-cp/README document the workflow.
Verified: editor loads with no console errors; both menu buttons + all four functions
present; zip contains editor.html. (FSA save needs a real user gesture to test on-device.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Place step circles by proportional position in the bar (beat = column start) instead
of centring in per-lane slots, so same-meter lanes' beats land at the same x (e.g. the
8-step hat's beats sit directly under the 4-step kick/snare). Cap circle radius at 6
(was up to ~18). Verified by printing per-lane beat x-positions + rendering the grid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
From on-board feedback (works well; minor tweaks):
- Pad grid uses circles now: big circle on each beat (division), small on the
subdivisions (vectorio.Circle — native, no extra cost), coloured/lit as before.
- Lane labels use a new small font (font_s.bin, ~12px via gen_font.py) so they're
half-size and show more of the voice name (e.g. 'hatClos').
- LED was blinding -> LED_BRIGHTNESS scale (default 0.15) applied on every write.
- Residual tearing -> SPI back to 62.5 MHz (vendor speed; smaller tear window on a
panel with no tearing-effect pin). Both are CONFIG flags.
Verified by rendering the full scene headless. font_s.bin added to gen_font.py + bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the single beat-dot row with a full pad grid: each lane is a row of step
pads coloured by dynamics (mute/normal/accent/ghost), with the playhead lit as it
plays (per-lane, so polymeter shows). Header (title/BPM/RUN/item) is compacted above
it; transport stays below. Pads are vectorio rects sharing one 8-colour palette and
recolour in place via color_index (cheap, tear-free); the grid only rebuilds on track
change. Caps at MAXLANES=5 rows (extra lanes still play). Verified by rendering the whole
displayio scene graph headless (layout + playhead lighting correct).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
From on-board feedback (memory + colours now good):
- LED: drive the WS2812 via the core neopixel_write module (no neopixel library to
install) — a tiny RGB class. Self-contained: it works straight from the bundle.
- Tearing: switch displayio to auto_refresh=False and push a complete frame only when
the scene changed (dirty flag, capped at the panel's refresh rate) so updates are
never shown mid-paint. Beat dots now recolour in place (vectorio color_index) instead
of being rebuilt every beat, shrinking the dirty region.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On hardware the app rendered, beeped and took input, then died with MemoryError at
the text Bitmap alloc — the two ~37KB base64 font strings stayed pinned in RAM. Move
the fonts to small binary files read at boot (font_m.bin / font_l.bin), drop the
base64 + binascii, and gc.collect() before each text bitmap. code.py 56KB -> 20KB and
RAM use drops ~37KB+. Also: cyan rendered as yellow (R/B swapped) -> MADCTL 0x40 -> 0x48.
Bundle + README updated to include the font blobs. (LED still needs the neopixel lib.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New pico-cp/ — a CircuitPython port of the PM_K-1 firmware so the Pico mounts as a
CIRCUITPY drive carrying its code + tracks (the MicroPython pico/main.py stays the
simple fallback):
- pico-cp/code.py: displayio BusDisplay driving ST7796 via a custom init_sequence;
smooth anti-aliased text via displayio Bitmap+Palette (reuses the baked font blobs);
vectorio rects for dots/buttons; DIY GT911 touch (16-bit regs, edge-detected);
pwmio buzzer, analogio joystick, digitalio buttons, optional neopixel RGB; the
polymeter engine on a time.monotonic_ns scheduler. Reads /programs.json (falls back
to baked defaults); CircuitPython auto-reloads on file change.
- pico-cp/programs.json: the 23 default grooves. pico-cp/README.md: flash + calibrate.
- build.sh/deploy.sh: bundle + serve /pm_k1_circuitpy.zip. info-kit.html: experimental
'CircuitPython edition — USB drive' section.
Verified in CPython (stubbed displayio): init sequence well-formed, parser handles the
grooves incl. (3,8) euclid + @-4 gain, and code.py's actual make_text renders identical
smooth AA text. Hardware bits (panel/touch/MIDI) await on-board testing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Four firmware improvements (web simulator already had these):
- Smooth lettering: replace the upscaled 8x8 bitmap font (and the 7-seg BPM) with
baked anti-aliased proportional fonts (DejaVuSans-Bold 22 + 78), rendered by
blending fg over bg through a 16-entry alpha LUT. Generator: pico/gen_font.py.
- All grooves: PROGRAMS now carries the full web set list (23 grooves), not 5.
- Fix: the RGB LED stayed lit when stopped — the stop path now zeroes the colour
and the fade only runs while playing (led_off()).
- Fix: the on-screen play button played one beep then stopped — a finger held past
the old lockout re-fired the toggle. Touch is now edge-detected like the hardware
buttons (fires once on finger-down, ignores held).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A user dropped main.py onto the RPI-RP2 BOOTSEL drive (which only accepts a .uf2)
and it vanished on reboot. Clarify in pico/README.md and info-kit.html that
flashing is two distinct steps: (1) drag-and-drop the MicroPython .uf2, then
(2) copy main.py over USB serial with Thonny/mpremote — the Pico is not a USB
drive once MicroPython is running.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The on-screen header showed VARASYS + the model; since the wordmark is already
silkscreened on the case/PCB, keep only the PM_K-1 KIT label on the display.
Applied to both pico/main.py (draw_static) and the kit.html web simulator.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A new, first-actually-buildable form factor for the 52Pi EP-0172 "Pico Breadboard
Kit Plus" (Raspberry Pi Pico; 3.5" ST7796 320x480 cap-touch via GT911, PSP
joystick on ADC0/1, WS2812 RGB on GP12, buzzer GP13, buttons GP14/15):
- pico/main.py — one self-contained MicroPython file: ST7796 direct-draw driver,
GT911 touch (16-bit register addressing), WS2812 RGB (neopixel), PWM buzzer,
ADC joystick, buttons. It parses the project's own program-string language
(verified against the web engine's semantics) and runs a non-blocking
ticks_us scheduler with an on-screen touch UI. CONFIG flags cover panel /
colour / touch / joystick calibration. pico/README.md has flashing +
calibration steps.
- kit.html — lean widget that mirrors the firmware's on-screen UI (portrait
320x480 canvas) plus a joystick / RGB / buzzer / A-B buttons; plays via the
shared engine. info-kit.html — the real EP-0172 pinout, a parts list
(~$45 incl. Pico) and the firmware to flash (downloads /pico-main.py, links
the README + source).
- Landing + embed page list the Kit; build.sh/deploy.sh build the two pages and
serve pico/main.py as /pico-main.py for download.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously every device .html bundled its full narrative (purpose, BOM,
dimensioned drawings, embedding docs) inside a #techinfo block that embed
mode (?embed=1) only CSS-hid — so every embedder and every landing-page
iframe downloaded all of it (showcase 77%, teacher 49% of the file) for
content no embedder ever sees.
Now:
- <device>.html is the lean widget only: header + front view/controls +
title + summary + program box. ?embed=1 still collapses to the bare
widget; the heavy narrative is gone from the payload.
- info-<device>.html (new, one per form factor) carries all the words —
purpose, dimensions, priced BOM, embedding docs — and embeds the live
widget at the top via the existing iframe + auto-resize protocol
(new shared src/infoembed.html + src/infoembed.js).
- Each device links out to its info page ("…dimensions & BOM →"); the
landing panes and viewport bar now offer both Open ↗ and Specs & info ⓘ.
- Dropped the now-dead "Show info" toggle (CSS + progbox.js).
Branding: adopt the official VARASYS "tagline on the bottom" logos from the
brand kit (light-background variant now matches; dark already did). The
tagline is baked into the PNGs, so remove the CSS .brand-tag / .dev-tag
spans and the showcase canvas-drawn tagline. Brand cyan #0AB3F7 / navy
#1C283F already match the official palette.
build.sh / deploy.sh: build + deploy the six new info-*.html pages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logos: the brand is now a consistent lockup — wordmark image + a crisp CSS
"Simplifying Complexity" tagline — in the shared header, device silkscreens
(teacher/stage/micro), the player (was a CSS text box) and the showcase canvas
(was drawn text; now the real logo image + tagline). Cropped the baked tagline
out of logo-light.b64 so both themes render the tagline once. Renamed device
silk logos to .dev-logo so they no longer shrink the shared header logo.
Embeds: every form factor now loads its default set lists when embedded with no
config — and the Concepts landing embeds them that way (viewport loads
<device>?embed=1 with no forced #p=; the program box reflects what the device
reports and only overrides on explicit Load).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Embed page: a form-factor dropdown that rewrites every snippet (drop-in +
plain iframe), the live demo, and the name for the chosen version; variant
table completed to all six.
- Display (Showcase): the tempo weight no longer flashes and is drawn BEHIND the
pendulum lights so it never hides a beat flash.
- Practice (Micro): a beat/sub-beat flash — the whole 14-seg display washes amber
on each step (subtle on sub-beats, brighter on the beat, full on the "1"),
latency-compensated like the other devices.
- Landing: Philosophy section moved above "Pick a form factor".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add front + top/side dimensioned schematic views (inch + mm) inside the
"Show info" section of Stage (front + top-edge I/O), Practice (front bar +
both end faces), and Display (pyramid front + side profile). Shared .dview /
.dschem CSS in base.css.
- Fix: progbox.js now defers to DOM-ready, so the "Show info" toggle + #techinfo
are wired even when they sit after the page script (Stage/Practice/Player were
silently not toggling / ignoring ?info=1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each form-factor page is now plain by default: title + summary + the front view +
a program I/O box (shared src/progbox.* — paste a patch OR a base64 set-list code,
decoded + linted + loaded; copy; reflects the device state and posts it to an
embedding parent). All technical content — description, BOM, dimensions, top/side
views, embedding — hidden behind a "Show technical info" checkbox; ?info=1 opens it
checked. Teacher's top-edge view + dimensions are marked .tech so they hide too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Editor: per-lane gain knob (drag/scroll/double-click, dB, applied at schedule
time — no stutter); program box now decodes base64 set-list codes + lints
(clear ✓/✗ message). When embedded it posts its program to the parent.
Landing (Concepts) rebuilt to the spec: description first, the EDITOR open by
default in a live viewport, a summary pane per form factor (click loads it into
the viewport), and a program I/O box that shows the current program decoded (not
base64), accepts plain text OR a base64 set-list code, lints it, loads it into
the viewport, and copies it. Viewport auto-sizes + reflects the device's posted
program. Engine codec inlined for decode/lint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove the VCSL sample kit entirely (editor 351K → 141K). All voices are
synthesized; the friendly GM names now alias to the punchier 808/909 renders
(KIT_ALIAS). build.sh drops the @BUILD:samples inlining; assets/samples.json gone.
- Conventions (backward-compatible): GM note-number aliases (36=kick…), '-'/'_'
rest aliases in step patterns, Euclidean (k,n[,rot]) shorthand.
- Per-lane gain in dB (@<db> in the grammar) applied as a velocity multiplier at
schedule time — no stutter; threaded through every host's buildMeters + the
editor's lanes (knob UI comes in Phase B).
- 15/15 engine round-trip tests pass; pages console-clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>