Skip to content

feat(sorting): sound — per-lane tones and a finish chime, with mute and volume - #22

Merged
benjamin-small merged 5 commits into
mainfrom
feat/sorting-sound
Sep 23, 2026
Merged

benjamin-small merged 5 commits into
mainfrom
feat/sorting-sound

Conversation

@benjamin-small

Copy link
Copy Markdown
Owner

Adds sonification to the sorting lab.

Engine (Rust)

  • Lane::last_touch(): the kind of the most recent op (compare, or write for writes and swaps) and the value it landed on
  • Rule summary carries size, last_kind, last_value per lane (null before the first tick)

Web

  • lib/sorting/audio.ts: planAudio() diffs consecutive summaries into per-lane tone/chime/rest events (pure); SortingAudio voices them on Web Audio with one persistent oscillator per lane (sine for compares, triangle for writes, pitch log-mapped over three octaves from the value touched), a two-note chime when a lane finishes, and a master gain into a compressor so 28 voices stay listenable
  • Controls bar gains a speaker toggle and a volume slider (disabled while muted). Starts muted; the click is the gesture that brings the AudioContext up
  • readSummary() accepts the new fields, treating wasm-bindgen's undefined for a Rust None as null
  • The shell's controls bar wraps instead of widening the page
  • README and docs/testing.md updated

Tests: 178 Rust, 127 web (17 new for the planner and wrapper against a stub AudioContext, 2 for the controls). Browser-verified: 28 voices build on unmute, a single lane runs with live counters, pitch updates flow, chime fires on finish, no horizontal overflow at 800px.

…alue

Lane::last_touch() returns the kind of the most recent op (compare, or
write for writes and swaps) and the value it landed on — the larger of
a compared/swapped pair, or the value written. The rule summary carries
it as last_kind/last_value (null before the first tick) plus the array
size, so the lab can voice each lane without the op traces.
…h mute and volume

lib/sorting/audio.ts: planAudio() diffs consecutive summaries into
per-lane tone/chime/rest events (pure, tested); SortingAudio voices them
on Web Audio with one persistent oscillator per lane (sine for compares,
triangle for writes, pitch log-mapped over three octaves from the value
touched), a throwaway two-note chime when a lane finishes, and a master
gain into a compressor so 28 voices stay listenable. Starts muted; the
speaker button's click is the gesture that brings the context up.

The controls bar gains a speaker toggle and a volume slider (disabled
while muted). readSummary() now requires the engine's size/last_kind/
last_value per lane. The shell's bar wraps instead of widening the page.
wasm-bindgen hands the engine's None across as undefined, not null, so
the strict null check rejected every lane that had not ticked yet and
the whole summary read as null — a single running lane never showed as
running and no voices were built until every lane had moved. Accept
either and normalize to null. README: describe the sound controls.
- pitchOf maps the engine's 1..=size value range (value 1 is the base
  note, value size the top) instead of 0..size-1, which had pinned the
  two largest values to the same note and never played the base
- planAudio no longer rests a running lane between ops, so a slow lane's
  blip rings out on its own envelope instead of being cut every frame;
  rests fire when a lane stops, is rewound, or the summary goes away
- the speaker button keeps a fixed accessible name ("Sound") with
  aria-pressed for state, rather than a changing label plus aria-pressed
- the chime's throwaway nodes disconnect themselves once they end
- tests for the no-advance, pause, and rewind cases; docs counts
@benjamin-small
benjamin-small merged commit a665f46 into main Sep 23, 2026
6 checks passed
@benjamin-small
benjamin-small deleted the feat/sorting-sound branch September 23, 2026 20: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