Web: live serve dashboard, GitHub Pages landing-page deploy, and a browser-version plan - #5
Merged
Merged
Conversation
…l offline use - Add audx/synth.py: pure-numpy procedural drum/perc voices (kick, snare, hh, oh, clap, rim, tom, cowbell, perc, ride, crash, shaker, sub) with aliases, tuning, and deterministic rendering. No samples required. - Renderer falls back to the synth kit when a sample is not found, so 'audx render "kick 4/4"' makes sound with zero setup; --sample is optional. - New commands: 'audx demo' (renders a full multi-track beat) and 'audx synths' (lists built-in voices). - Engine: import sounddevice lazily so the CLI and all offline features work without the PortAudio system library; add SynthVoice + synth fallback to the live callback; tidy types. - Types: mypy now checks the whole package cleanly (was 4 files); fix real errors in midi/web/calibration/voice/groove; add types-requests.
- Add test_synth, test_render_synth, test_cli, test_more_coverage covering the synth kit, synth-fallback rendering, MIDI export, project round-trips, DSL operators, and the offline CLI surface. Coverage 27%→43%. - Fix 'audx mix set <ch> gain -3': allow dash-leading values (ignore_unknown_options) so documented negative-dB usage works. - Autouse fixtures reset the process-global pattern engine between CLI tests. - Raise CI coverage gate 25→40.
…page - Rewrite README around the 10-second zero-config demo and the built-in synth kit; add badges, DSL reference table, grouped command overview, honest 'what it isn't yet' section. - Add LICENSE (MIT), CHANGELOG.md (Keep a Changelog), CONTRIBUTING.md. - Add docs/getting-started.md, docs/synth-kit.md and a docs index. - Add site/ static landing page (dark terminal aesthetic, no build step).
CI was pip-installing an unpinned mypy (2.1.0) and using flake8, so it diverged from the project's locked tools and failed on stricter var-annotated/unused-ignore inference that 'uv run mypy' (1.20.2) doesn't report. Run ruff, mypy and pytest through uv with --all-extras so CI matches local exactly; drop flake8. fail-fast: false to see all matrix results.
…ideo + PyPI release Product (team): - synth: 6 melodic voices (bass, pluck, stab, keys/ep, saw, sine) — band-limited saws, pitched via tune; bass is now its own voice. Kit is 20 voices total. - arrangement: Song/Section model + render_song; new 'audx song render|info' build multi-section tracks (intro/verse/drop/outro) from a JSON spec. - ui: live finger-drum pads (w e r a s d f z x c u i o) trigger synth voices on dedicated channels while a project is open. Marketing: - Animated terminal demo GIF in README (scripts/make-demo-gif.py, Pillow-only). - Runnable Remotion promo project (marketing/remotion/) + rendered 1080p MP4 and poster (marketing/promo/). Packaging: - Automated PyPI release via trusted publishing on v* tags (.github/workflows/release.yml); PUBLISHING.md updated for the uv build flow. Tests 123→172, coverage 43%→46%; ruff + mypy clean across the package.
The old promo was static terminal text with hard cuts. Rebuilt it around what audx actually is — a live instrument: - build-beat.py renders a real audx beat (synth kit) and emits a frame-accurate data track (per-track 16-step grids + hit frames, master RMS envelope, waveform peaks) so the motion graphics are driven by the actual audio. - AudxPromo.tsx: a live step-sequencer grid that fires on the beat with a moving playhead, per-track VU meters, and a waveform scope that sweeps in time — with the rendered beat as the MP4 soundtrack. Smooth eased crossfades, no hard cuts. - Electric-neon palette on pure black (cyan/magenta/lime/amber/violet). - README hero is now the neon sequencer poster linking to the 20s promo. Rendered artifacts: marketing/promo/audx-promo.mp4 (1080p, with audio) + poster.
… + gradients Neon + gradients read as cheap/generic. Researched premium audio-brand and motion direction (Teenage Engineering's muted-palette + single-accent restraint; the 2025 monochrome counter-trend to gradients/glow) and rebuilt accordingly: - Near-monochrome: warm off-white on true black, a SINGLE amber accent used only for the play position. No gradients, no glows, flat fills. - Sequencer is now a Monome/TE-style monochrome LED matrix: hairline-outlined cells, off-white hits, amber only under the thin playhead rule. Flat waveform. - Swiss layout: passe-partout frame, lowercase monospace labels, generous space. - Calmer motion: one bezier easing, restrained fades, discrete caret blink. - Flat monochrome also compresses far better — MP4 2.7MB (was ~10MB). Refreshed marketing/promo/*.mp4 + poster and docs/assets/promo-poster.png.
- audx jam: real-time play from a MIDI controller or Push 2. Drums mode maps
GM notes to drum voices and never leaves a pad silent; --chromatic plays a
melodic voice across the keys. New audx/live.py (pure, tested mapping) wired to
engine.play_synth (now supports tune_semitones).
- docs/playing-live.md: kid-friendly controller/Push 2 walkthrough; linked from
README ('Play it live') and the docs index.
- Bump to 0.3.0 (pyproject, package.json, __init__, CHANGELOG cut).
- Push2Lights drives Push 2 pad LEDs over MIDI: SysEx colour-palette entries (8-bit RGB split to 7-bit LSB/MSB) + note-on (channel 0) lighting, per the official Push 2 MIDI spec. Byte-exact unit tests against the spec example. - push2_pad_layout: a 13-voice drum kit on the bottom two rows, one colour each. - audx jam auto-detects a Push 2, paints the kit and flashes each pad on hit (--no-lights to disable; lights are drums-mode only). New 'audx push2 lights' lights the kit on its own for a quick check. - Bundle python-rtmidi so mido has a backend — midi/jam/Push 2 work out of the box. - Port discovery degrades gracefully with no backend/device; guide + README + CHANGELOG updated.
… plan + v0.3.0 notes - audx serve: upgrade the read-only dashboard to a live step-sequencer grid with a moving playhead + per-channel meters, in the monochrome house style (stdlib only). New /state shape (per-track 16-step grids); tests in test_web.py. - Landing page goes live: .github/workflows/pages.yml deploys site/ to GitHub Pages (URL https://totalaudiopromo.github.io/audx/ once Pages source = GitHub Actions). vercel.json + .vercelignore included for the Vercel git-integration alternative. - docs/web-version-plan.md: phased, honest plan for a browser-playable audx (Pyodide demo → TS/Web Audio core + Web MIDI/Push 2 → parity), with effort/risks. - docs/releases/v0.3.0.md: prepared release notes.
Standalone 'audx serve' reads its own (empty) process state, so it only shows a session running in that same process. Add web.serve_in_background() and wire 'audx open --serve [--serve-port]' so the TUI hosts the dashboard in-process — the session you play now shows up live at http://<machine>:8080/ on any device on the LAN (read-only). Real HTTP round-trip test; guide + README updated.
…attack plan - site/play.html: a Pyodide playground that runs the REAL audx DSL parser and synth kit client-side (numpy in the browser), mixes to stereo and plays via Web Audio. Presets, WAV download, nothing leaves the visitor's machine. - site/audx_web/webrender.py: render path that dual-imports the real audx package (tests/dev) or flat modules (Pyodide), so the in-browser sound matches the CLI. - No drift: scripts/sync-web-modules.sh copies synth.py/pattern.py into the bundle; pages.yml runs it on deploy and triggers on src changes; a test asserts the bundled copies equal src/ (tests/test_webrender.py). - 'play it in your browser' CTA on the landing page; README pointer. - docs/web-version-plan.md: Phase 1 marked shipped; phases 2-3 expanded into a concrete milestone attack plan (golden-vector tests, TS synth+DSL, Web Audio scheduler, Web MIDI + Push 2 LEDs, then parity/sharing).
The fidelity-critical core of the browser port. web/src/dsl.ts faithfully ports
src/audx/pattern.py (steps, modifiers, swing), validated against fixtures emitted
from the REAL parser so the browser parses identically to the CLI.
- scripts/gen_web_fixtures.py: emits web/fixtures/{dsl,swing}.json from audx.pattern.
- web/: vitest project; tests/dsl.test.ts asserts TS == Python within 1e-9 across
30 cases — every grammar branch plus the quirks (16x8 = 16 hits, banker's
rounding in swung_beat, sample aliases bd->kick/hh->hihat, modifier clamping).
- tests/test_web_fixtures.py: guards the committed fixtures against parser drift.
- ci.yml: new 'web' job regenerates fixtures, fails on drift, runs tsc + vitest.
- docs/web-version-plan.md: M2.2 marked shipped; M2.1 (synth port) is next, reusing
the same golden-vector harness for per-voice audio references.
Port all 20 synth voices to TypeScript (web/src/synth.ts) and build a real browser instrument on top of them. Synth port, golden-vector verified: - 10 noiseless voices match numpy sample-for-sample (mean err < 2e-4); cowbell + the 9 noise voices match structurally (normalized peak, envelope correlation, energy); the repitch resampler is checked — 44 vitest assertions. - scripts/gen_web_fixtures.py emits web/fixtures/synth.json from the REAL synth. - Fixed a latent bug: 'perc' had no 'percussion' alias, so pattern.py's perc steps dropped silently in the synth (and Phase 1). Added the alias in src + TS. audx studio (site/studio.html + web/src/studio.ts, bundled to site/studio.js): - 16-step grid sequencer driven by the native synth. - Sample-accurate Web Audio lookahead scheduler (25ms tick / 100ms ahead) with a real swing offset on odd 16ths. - Per-track mute/solo/volume, add/remove voices, drag-to-paint, audition on click, spacebar transport, master meter. - 'copy as audx DSL' exports the grid as a pattern you can paste into the CLI. - Brand-cohesive monochrome+amber UI, responsive. - esbuild bundles the app; CI (web job) typechecks + tests + builds; Pages deploy builds site/studio.js. Landing hero now leads with 'open the studio'. - docs/web-version-plan.md: M2.1, M2.3, M2.4 marked shipped.
Make the studio session durable, shareable and exportable — all logic verified in vitest (no browser needed). - web/src/project.ts: compact, URL-safe encode/decode of a session (voices as indices, steps as a 16-bit mask, flags). Round-trip tested. - web/src/render.ts: offline render of the loop to a normalized stereo buffer + 16-bit WAV, reusing the native synth and the live scheduler's 16th-note + swing timing so the file matches what you hear. Length/mute-solo/silence/header tested. - web/src/types.ts: shared Track/ProjectState + audibleTracks (solo-then-mute). - studio: 'share link' (whole groove in the URL hash), 'download wav', and localStorage autosave that restores on load (hash link wins). Transport inputs sync to a loaded session. - 81 web tests pass; tsc clean; esbuild bundle rebuilt. - docs/web-version-plan.md: M3.3 + M3.5 marked shipped.
Bring the signature audx hook to the browser: play voices from a controller and light up a Push 2, using the exact same MIDI bytes the CLI sends. - web/src/push2.ts: TypeScript port of audx/push2.py — Ableton user SysEx palette entries (RGB split into 7+1 bits), reapply, and note_on pad lights. Golden-vector tested against the real Python module (scripts capture mido's wire bytes via a recording port) — setup stream, flash, and tick-restore all byte-exact. - web/src/midi.ts: thin Web MIDI glue — pads (notes 36..48) audition voices, the transport notes (85/86) drive play/stop, and a connected Push 2 is set up + lit. - studio: 'connect MIDI / Push 2' button; the sequencer flashes pads as it plays; LED revert ticked each animation frame. Hidden when Web MIDI is unavailable. - Extended the Python fixture drift-guard to cover synth.json + push2.json too (matters now that GitHub Actions CI isn't triggering for the PR). - 85 web tests pass; tsc clean; bundle rebuilt. docs: M2.5 marked shipped.
…track pan Deepen the instrument with a v2 session model (all logic vitest-verified): - Per-step velocity: ghost/normal/accent levels (right-click a cell to cycle); the scheduler and offline render scale gain per step. - Multi-bar patterns: 1/2/4 bars (16/32/64 steps); grid, scheduler and render all honour the length, with barline gaps in the grid. - Per-track pan: equal-power law matching StereoPannerNode, applied live (a StereoPanner per hit) and in the offline render so the WAV matches playback. - types.ts: v2 Track (steps as number[] velocities, pan) + ProjectState.bars, panGains, resizeSteps. - project.ts: v2 URL encoding — per-step velocity as /15 hex digits (round-trips exactly for the discrete levels), bars, pan. Rejects v1/garbage. - render.ts: velocity + pan + multi-bar; normalizes on both channels. - studio: pan slider, bars selector, accent cycling, velocity-styled cells; resize steps when bars change. - 87 web tests pass (project round-trip incl velocity/pan/bars; render pan + velocity + multi-bar length); tsc clean; bundle rebuilt. docs: M3.1 expanded.
… fallback) Drop an audio file on a track (or hit the file button) and it plays instead of the synth voice, mirroring the CLI's precedence (a resolvable sample wins, else the built-in synth — arrangement._voice_audio). - web/src/samples.ts: content-addressed SampleStore; decodeAudioData; raw bytes persisted in IndexedDB so samples survive a reload; sampleStereo() feeds the offline renderer. Pure ref-hashing is testable; decode + IDB are browser-only. - types.ts: Track.sampleRef/sampleName + trackUsesSample() precedence helper. - project.ts: v3 wire carries sample ref + name only (audio stays local); share links degrade to synth on a machine without the bytes. Rejects v2/garbage. - render.ts: optional SampleProvider places stereo sample data (pan/velocity) over the synth path; backward compatible. - studio: per-track 📁 button + drag-a-file-on-a-row, audition uses the track's buffer, WAV export passes the provider, samples reloaded from IDB on audio start. - 94 web tests (new types.test.ts: precedence/pan/audible/resize; render: sample provider + fallback; project: v3 round-trip incl sample ref). tsc clean; built. docs: M3.4 marked shipped.
Export each audible track as its own WAV, like 'audx render --stems'. - render.ts: refactor the placement loop into a shared placeTrack() used by both renderProject (sum + normalize) and the new renderStems() (one normalized WAV per audible track, named NN-<voice|sample>.wav; honours sample/velocity/pan/multi-bar). - web/src/zip.ts: tiny store-only PKZIP writer (local headers + central directory + EOCD, CRC32 per entry) — no dependency. 'download stems' bundles the stems into audx-studio's audx-stems.zip. - studio: 'download stems' button + shared download() helper. - 100 web tests: renderStems (count/naming/headers, muted excluded), zip structure + canonical CRC32 (0xCBF43926), renderProject unchanged after the refactor. tsc clean; bundle rebuilt. docs: M3.5 marked shipped.
…erop
Arrange studio grids ('scenes') into a sequence, mirroring arrangement.py's
Section/Song model — with real web<->terminal interop.
- web/src/song.ts: Scene/Song types, timeline() (cumulative bar offsets) and
totalBars; CLI Song JSON import/export ({bpm, sections, sequence}). The DSL bridge
reuses the golden-vector-tested parser: trackToDSL emits a grid line, dslToTrack
rebuilds a track via parsePattern (tiling 1-bar patterns across section bars, CLI
repetition semantics). A web song plays with "audx song render", and CLI songs open
in the studio.
- render.ts: renderSong lays sections out along the timeline (bounced for play/WAV),
honouring sample/velocity/pan/swing per scene.
- studio: a 'song' panel — save grid as scene, click scenes into a sequence, play
(bounce), download song WAV, export/import Song JSON; persisted to localStorage.
- Parity: gen_web_fixtures.py emits song.json from Song.from_spec().timeline();
web/tests/song.test.ts asserts timeline parity, JSON round-trip and section
placement (a hit in section 2 lands after section 1). 112 web tests; tsc clean.
- Caveat (documented): CLI explicit grids are binary, so per-step accents are lossy on
a CLI round-trip; multi-bar scenes export as tiled 1-bar patterns.
docs: M3.2 marked shipped — Phase 2 + Phase 3 of the web plan now complete.
chrisschouk
force-pushed
the
claude/audx-web-and-serve
branch
from
June 21, 2026 17:52
c543e9b to
067f691
Compare
Song mode played by bouncing to one AudioBuffer; make it first-class by driving the existing 25ms/100ms lookahead scheduler across the song timeline. - song.ts: songStepPlan(song) — pure mapping of each global 16th-step to the active scene + local step (walks timeline()); length = totalBars*16. Unit-tested. - studio.ts: extract scheduleHits(tracks, localStep, time, swing) shared by the pattern and song transports. A songPlan var routes the scheduler through the song (loops); transportSteps() picks the modulus. 'play song (live)' now schedules live — instant stop, Push 2 pads flash for the active scene, and the active scene/sequence chip highlights with a 'scene · bar x/total' readout. Pattern-play and song-play are mutually exclusive. The bounce stays behind song WAV export. - 108 web tests (new songStepPlan offset + repeat tests); tsc clean; bundle rebuilt. Note: first push since Actions was re-enabled, so this is the first clean-checkout CI run for the branch.
main advanced via PR #4 ("Make audx marketable"), which added the same foundational files this branch already carries (synth.py, push2.py, arrangement.py, site/, …) in a parallel version — producing add/add conflicts. This branch is a strict superset of those files (it also rewrote the serve dashboard and added the whole web/ studio), so the 5 conflicts (README.md, docs/README.md, docs/playing-live.md, site/index.html, synth.py) were resolved to this branch's versions, which contain main's content plus the web additions (browser callout, open --serve, percussion alias, second-screen doc). Verified after merge: ruff + mypy clean, full Python suite green, 108 web tests pass, tsc clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Answers "can I use audx in the terminal and at a URL?" — three things, scoped honestly.
1. A live dashboard URL (
audx serve) ⬆️Upgraded the read-only companion from plain level-bars into a live step-sequencer grid — per-track 16-step cells with a moving amber playhead, plus per-channel meters, in the same monochrome house style as the promo. Pure stdlib, localhost-only.
audx serve # → http://127.0.0.1:8080 (watch on your phone while you play)New
/stateshape (per-track step grids); covered bytests/test_web.py.2. The landing page, live at a public URL 🌐
.github/workflows/pages.ymldeployssite/to GitHub Pages on push tomain.One-time setup (yours): repo Settings → Pages → Source = "GitHub Actions". After that it publishes automatically to https://totalaudiopromo.github.io/audx/.
vercel.json+.vercelignoreare included too, so you can alternatively connect Vercel's git integration (it servessite/statically, ignoring the Python/marketing tree). (The Vercel MCP here can't deploy without your CLI auth, so I set up the path you control.)3. A real browser-playable audx — planned, not hand-waved 📋
docs/web-version-plan.mdlays out the honest path: audx is Python (numpy/PortAudio/Textual/mido), so a web version is a core port, not a deploy flag. Three routes (Pyodide / TypeScript+Web Audio / Rust-WASM), a recommended phasing (Pyodide "try it" demo → TS+Web Audio live core with Web MIDI + Push 2 LEDs → parity), golden-vector tests to keep native and web in sync, and effort/risk estimates.Also
docs/releases/v0.3.0.md— prepared release notes (you can paste these into the GitHub release when you tagv0.3.0).Verification
ruffclean ·mypyclean (39 files) · 199 tests pass ·_state()produces correct grids (kick = four-on-the-floor, etc.).The honest status (terminal vs URL)
audx serve.🤖 Generated with Claude Code
Generated by Claude Code