Skip to content

Draw the routing view as a manual's flow diagram, lit by the sound (t-146) - #115

Merged
JohnD-EE merged 6 commits into
mainfrom
feature/t146-routing-diagram
Oct 29, 2026
Merged

JohnD-EE merged 6 commits into
mainfrom
feature/t146-routing-diagram

Conversation

@JohnD-EE

Copy link
Copy Markdown
Contributor

What

The routing view (t-142) read as a pastel web-app chart. It now reads as the flow diagram in a service manual, and it lights up with the sound actually going through the synth (t-146).

  • Faceplate, not a card. The diagram sits between the rails at their width, in the panel's own colours: the faceplate and its silkscreen ink in Hardware mode, black on paper in Outline (the page wraps it in railStyle). The module colour is kept to a small lamp in each block's corner.
  • Manual-style blocks. Each block is a square box. A cell on its left holds a schematic symbol (oscillator, noise, mixer Σ, low-pass curve, VCA triangle, ADSR, LFO, MIDI DIN, speaker…) and a designator (VCO, VCF, EG…). Names are in the panel's condensed caps; the key and the caption are in mono, on a faint grid. The symbol is chosen by module, or by a new optional symbol on RoutingBlock.
  • Routes vs cables. The synth's own routes are fine ink lines with arrowheads: audio heavier, CV light, gate dotted. The player's cables are drawn on top of them as cables: thick, in their colour, with a dark casing and a jack at each end.
  • Lit by the sound. "Solid" still means the patch routes it (routingState()). On top of that, routingActivity() (lib/app/synths/lib/routing-activity.ts) turns the engine's meters and the output level into a glow for each part: the lamp, the box tint, a halo, and coloured flow along the lines. The glow fades as the sound does. Each block follows a new optional meter:
    • the gate for MIDI, and each contour's envelope;
    • the LFO, and Oscillator 3 at LO, pulse with their actual wave;
    • at audio pitch, an oscillator glows with the output times its share of the mixer;
    • the rest of the audio path follows the output level (read off the analyser, as the scope does);
    • the buses follow the brightest line into them.
  • Engine meter. The DSP meter now also carries the LFO's rate and each oscillator's output, pitch and mixer level (lfoHz, oscV, oscHz, oscLv).

Why this shape

  • Pure targets plus a frame loop, not React state. routingActivity() is pure and unit-tested. The view eases towards it once a frame with settle() (up in about 40 ms, down over about 450 ms, or quickly both ways for a moving wave) and writes --a straight to each element.
  • A slow wave becomes a steady glow when fast. The meter arrives 25 times a second, so a faster LFO would alias into a false slow pulse. Between 3 and 6 Hz the pulse blends into a steady glow.
  • Halo opacity, not an animated drop-shadow. Changing the shadow every frame re-rasterises a blur; changing an opacity only repaints a stroke.

Review rounds

Two /code-review rounds, each fixed in its own commit.

  • Round 1:
    • oscLv now includes CV into the level input.
    • A switched-out oscillator no longer glows with nothing playing.
    • The loop's refs are set as each render commits.
    • Each part's element is found once per change, and glows are kept in WeakMaps.
    • Cables are keyed by their jacks, so a glow stays with its cable.
    • The halo replaced the animated filter, and link ends are worked out once.
  • Round 2:
    • The oscillator meters reuse the same arrays, so the audio thread makes no garbage.
    • The level counts the oscAssign note gain, as the mix does.
    • The analyser is read only while the power is on.
    • The element lookup runs only when the parts change, and the routing is read through a ref.
    • The key's cable swatch matches an uncoloured cable.
  • Declined:
    • Caching the audio set and edge list across frames: it is a few dozen small allocations a frame, and caching would split the logic across the view.
    • Measuring names for wrapping: every Model D name fits, as the screenshots show, and no synth has a long one-word block name yet.

Gates

  • /pre-pr:
    • Type-check, CHANGELOG, Node version and router mocks pass. eslint and prettier pass, scoped to the changed files.
    • No barrel exports changed. No CHANGELOG entry is needed: lib/app is the fork's own tier, not Sunrise's public surface.
    • The anti-pattern scans are clean, each checked with a sentinel.
    • Coverage: routing-activity 100% of lines, routing-view 98.9%, routing-layout 98.5%.
    • The 449 routing, audio, engine and store tests pass.
  • Under load: with the machine at a load average of about 30, the scoped run timed out 16 synth-page tests. Run on their own, they pass. One routing page test still times out under that load, and main fails it the same way, so it is the machine, not this branch.
  • /security-review: no findings.
  • /code-review: two rounds, as above. Format: prettier, scoped.

Not done / open

  • Eye check is the owner's. The still diagram was checked in headless Chrome in both modes. Headless Chrome would not play a note, so the glow, the fade and the LFO pulse are covered by tests and await the end-of-build eye check.
  • Reload after merge. The worklet bundle is git-ignored and rebuilds on predev. A dev server that is already running needs npm run build:audio-workers and a page reload.

🤖 Generated with Claude Code

JohnD-EE and others added 6 commits October 29, 2026 17:28
…-146)

The diagram takes the faceplate's colour and ink between the rails (paper and ink
in Outline). Blocks are square boxes with a schematic symbol and designator, the
module colour kept to a lamp. The synth's routes are ink lines with arrowheads;
patch cables are drawn over them cased, in colour, with jacks.

routingActivity() turns the engine's meters and the output's level into a glow
per part over the routed path; the view eases it once a frame into --a (up fast,
down slow) so a release fades the path with the sound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ew (t-146)

The DSP meter now carries the LFO's rate and each oscillator's output, pitch and
mixer level. The LFO, and an oscillator slow enough to see (Oscillator 3 at LO),
glow with the wave itself, eased quickly both ways so the trough is drawn; from
3 to 6 Hz the pulse blends into a steady glow, since a 25 Hz meter would alias a
faster wave into a false slow pulse. An oscillator at audio pitch glows with the
output times its share of the mixer, and at half while it runs switched out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Meter an oscillator's level as the mix hears it, CV into its level input
  included, and build the arrays without a slice.
- A switched-out oscillator feeding the mod bus glows with the output, so it
  goes dark with the sound rather than holding at half.
- Set the loop's refs, and find the parts' elements, as each render commits
  rather than after it; keep each glow by element in a WeakMap, and key cables
  by their jacks so a glow stays with its cable when an earlier one comes out.
- Glow as a ring whose opacity follows --a, not an animated drop-shadow, so a
  frame repaints a stroke instead of re-rasterising a blur.
- Work out each link's ends once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Write the oscillator meters into three arrays per voice rather than new ones,
  so the audio thread makes no garbage; count the oscAssign note gain in the
  level, as the mix does.
- Read the analyser only while the power is on; work out the loudest
  oscillator once per call.
- Find the parts' elements only when the drawing's parts change, and read the
  routing through a ref with the state and source.
- Draw the key's cable swatch in the colour an uncoloured cable takes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JohnD-EE
JohnD-EE merged commit 6608c34 into main Oct 29, 2026
19 checks passed
@JohnD-EE
JohnD-EE deleted the feature/t146-routing-diagram branch October 29, 2026 19:19
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