Skip to content

Light the code as it plays, and cover every voice - #29

Merged
spacedevin merged 2 commits into
mainfrom
feat/code-lighting-and-all-voices
Sep 1, 2026
Merged

Light the code as it plays, and cover every voice#29
spacedevin merged 2 commits into
mainfrom
feat/code-lighting-and-all-voices

Conversation

@spacedevin

Copy link
Copy Markdown
Owner

Follows #28. Two things it left undone.

The player animated a progress bar and nothing else

Deckard and the promo videos light the step under the playhead and the instrument that is sounding
— most of what makes a step sequencer readable — and the site had none of it.

The highlighter now emits positions it already knew about: data-step on each step in a lane, and
data-track on every line, counting track headers in source order. That is the order parseSong
builds channels in, so a trigger's bus maps straight back to the lines that describe it.

<deck-player> finds the highlighted block beside it and, on each step, marks the step under the
playhead in every lane and tints the lines of each track sounding on it. Lanes are modulo their
own
length, so a 16-step and a 32-step lane sit at different places in their own patterns — which
is what the sequencer does with them. Both classes come off on pause, so a block at rest reads
exactly as before, and a <deck-player> with no code beside it finds nothing and lights nothing.

The element owns the audio, the page owns the markup, and they meet only at those two attributes.

Verified by stepping the playhead and reading back what lit:

s0  lit=[0,0]  live=[0]      s4  lit=[4,4]  live=[0]
s1  lit=[1,1]  live=[]       ...
s2  lit=[2,2]  live=[1]      s15 lit=[15,15] live=[]
s3  lit=[3,3]  live=[]       s16 lit=[0,0]  live=[0]   <- wraps

and by screenshotting a real playing block in a headless browser: at step 0 the kick's step is
boxed, its five lines are tinted, and the hat's lines are not — its step 0 is a rest.

An example for every voice

Coverage was 22 of 33. Missing were noiseBurst, fmTone, aether, formantVocal, chiptune,
cymbal, obSync, laserSync, patch, and the two speech voices.

Adds sections for percussion and metal, two-operator FM, drifting textures, sung vowels, the
generic chip voice, the rest of the sync family, and a hand-wired patch graph — the one voice
with no fixed architecture, where the gen_block names its own oscillators, filters and envelopes.

ttsVocal and meSpeakVocal are documented rather than pretended to be ordinary. Both reach
outside the audio graph — the Web Speech API, and the meSpeak engine's assets — so they need host
support and render as silence offline. Saying so is more useful than a Play button that does
nothing.

The intro no longer claims the player synthesizes only three generators faithfully. It carries the
whole catalogue now, and nothing on the page is substituted.

Verification

  • 33/33 voices have an example.
  • 46/46 player tests pass.
  • 29 of 31 blocks build a real graph with zero substitutions. The two that don't are the speech
    blocks, failing on window is not defined in Node — exactly the limitation the new section
    documents.
  • Ten blocks, covering the voices written from scratch here, were rendered to audio through
    scripts/render-wav.mjs to confirm they sound rather than merely parse — including the
    hand-wired patch graph at −8.4 dBFS peak.

The element animated a progress bar and nothing else. Deckard and the promo videos light the step
under the playhead and the instrument that is sounding, which is most of what makes a step
sequencer readable, and the site had none of it.

The highlighter now emits the positions it already knew about: data-step on each step in a lane,
and data-track on every line, counting track headers in source order — the order parseSong builds
channels in, so a trigger's bus maps straight back to the lines that describe it.

<deck-player> finds the highlighted block beside it and, on each step, marks the step under the
playhead in every lane and tints the lines of each track sounding on it. Lanes are modulo their
own length, so a 16-step and a 32-step lane sit at different places in their own patterns, which
is what the sequencer does with them. Both classes come off on pause, so a block at rest reads
exactly as before, and a <deck-player> with no code beside it finds nothing and lights nothing.

The element owns the audio and the page owns the markup; they meet only at those two attributes.
Coverage was 22 of 33. The gaps were noiseBurst, fmTone, aether, formantVocal, chiptune, cymbal,
obSync, laserSync, patch, and the two speech voices.

Adds sections for percussion and metal, two-operator FM, drifting textures, sung vowels, the
generic chip voice, the rest of the sync family, and a hand-wired patch graph — the last being the
one voice with no fixed architecture, where the gen_block names its own oscillators, filters and
envelopes.

ttsVocal and meSpeakVocal are documented rather than pretended to be ordinary. Both reach outside
the audio graph — the Web Speech API and the meSpeak engine's assets — so they need host support
and render as silence offline. Saying so is more useful than a Play button that does nothing.

The intro no longer claims the player only synthesizes three generators faithfully; it carries the
whole catalogue now, and nothing on the page is substituted.

33/33 voices now have an example. Every block was checked for substitutions and for building a
real graph; ten of them, covering the voices written from scratch here, were rendered to audio
through scripts/render-wav.mjs to confirm they sound rather than merely parse.
@spacedevin
spacedevin merged commit f18ba60 into main Sep 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant