Skip to content

feat: Notes & Chords lab - #32

Merged
benjamin-small merged 13 commits into
mainfrom
feat/notes-chords-lab
Oct 5, 2026
Merged

benjamin-small merged 13 commits into
mainfrom
feat/notes-chords-lab

Conversation

@benjamin-small

Copy link
Copy Markdown
Owner

Summary

A fourth lab, Notes & Chords (#/notes), built from the "every note is a vibration" explainer:

  • Pick one to three notes (C4…C5). Each swings a dot on its own bar: the first on the left (y), the second on the top (x), the third on the right (depth).
  • Two notes draw the Lissajous loop of their interval with dotted guides from each bar's dot to the pen; a 3 : 2 fifth closes after two swings of the first note. Three notes draw a 3D curve inside a slowly turning wireframe cube (drag to orbit).
  • A slim waveform strip plots each note's sine and their sum with a playhead.
  • The playback clock counts swings of the first note, so the Swings per second slider (0.25–1000 Hz, log scale) is literally the pitch; Real pitch ramps to the note's true frequency and the bars light solid once the dot moves faster than the eye follows.
  • Sound voices the notes with Web Audio once past about 20 swings per second; Pure ratios closes the loop, Piano (12-TET) drifts and never quite does.
  • Share links: #/notes?n=60,67&t=equal. A home tile shows C4 + G4 closing in two swings.

Design: docs/superpowers/specs/2026-10-04-notes-and-chords-design.md. Plan: docs/superpowers/plans/2026-10-04-notes-and-chords.md.

Shared changes

  • pixel_projection moves from the sorting viz into render/camera_2d.rs.
  • The AudioContext factory and the test stub are shared between the sorting and notes labs (web/src/lib/audio/context.ts, web/src/lib/test/stubAudio.ts).
  • LabShell's overlay no longer swallows canvas pointer events (pointer-events: none; the sorting grid's buttons already opt back in).
  • buildQuery keeps raw commas in share links.
  • Home grid is 2×2 for four tiles.

Tests

Suite Result
cargo test --all-features 247 passed
wasm-pack test --chrome --headless 33 passed
npm test (vitest) 371 passed, 30 files, 91.0% statements
cargo fmt --check, cargo clippy, npm run check clean (clippy warnings unchanged from main)

Browser-checked at 1280 px and 375 px, light and dark: one/two/three notes, Real pitch, Sound (one context, one oscillator per note), Piano labels, share links, the home tile's Done state.

…se and summary

Lift pixel_projection out of the sorting viz into render so the notes
viz can share it.
…pers

Share the AudioContext factory and the stub context between the sorting
and notes labs.
…ame cannot flash the dots; playhead fades too
- The story reads the ratio the way the readout does (2 : 3) and names the fifth.
- LabShell's overlay no longer swallows canvas pointer events; NotesLab drops its :has() workaround.
- The strip's segment count is capped like the trail's, and its time block holds while the figure is smeared so chords with long periods do not re-phase every frame at audio rates.
- Each voice's gain is gated by its own pitch, so a note below the root is not voiced while sub-audible.
- Full picker chips are aria-disabled (still focusable); a teardown test; lazy 3D resources; README tree line; testing.md re-measured.
…says when the swings are still too slow

VOICE_LEVEL was sized like one sorting blip (which sum across 28 lanes);
one to three sustained sines at that level peaked near -24 dBFS after the
compressor, which reads as silence on laptop speakers. At 0.8 shared across
the notes the output sits near -11 dBFS at the default volume. A hint beside
the Sound button now explains the silence below about 30 swings a second.
…o its defaults

- Deselecting the last note is allowed: the rule and viz accept an empty
  list (the stage is left clear and the page shows "Pick a note to start"),
  Real pitch is disabled, and the link spells it n=none.
- The Restart button becomes Reset: one note (C4), pure ratios, half a swing
  a second, from the first swing, playing.
- A config push reads the new summary at once rather than on the next frame.
@benjamin-small
benjamin-small merged commit f9024b2 into main Oct 5, 2026
6 checks passed
@benjamin-small
benjamin-small deleted the feat/notes-chords-lab branch October 5, 2026 00:06
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