Visualization gallery + Melody's Path - #16
Conversation
Sub-project 1 of the structure-revealing viz roadmap (#2, #3): a switchable visualization gallery (registry + VizPicker + persisted viz setting + the loadTimeline contract extension) plus the first new viz, Melody's Path — a scrolling per-track melodic contour. Frontend-only; vitest for geometry helpers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9252a9d386
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ro.observe(host); | ||
| viz.resize(host.clientWidth, host.clientHeight); | ||
|
|
||
| let ptr = 0; |
There was a problem hiding this comment.
Seed the scheduler when switching mid-song
Switching vizId while a song is already playing rebuilds this effect with ptr back at 0. On the first RAF at a nonzero head.time, the loop below emits onNoteOn for every historical note with start_sec <= time; in Melody's Path those all become blooms (and Cosmic still processes/caps hundreds of orbs), so switching visualizations mid-song can flash or stall on dense MIDI files. Initialize the new scheduler from the current playhead instead of replaying the whole past.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 420fce3 — good catch. The scheduler now seeds at the current playhead without firing events on the first frame after a viz/song switch, and the same guard covers time discontinuities in both directions (backward seek — which had this replay-flood pre-PR — and >1s forward jumps, same root cause Codex didn't flag). In-flight notes get their balancing onNoteOff before the reset, then ptr fast-forwards past the skipped span silently.
|
|
||
| /** Notes whose start_sec is within [t0, t1] (inclusive). `sorted` must be ascending by start_sec. */ | ||
| export function windowSlice(sorted: Note[], t0: number, t1: number): Note[] { | ||
| return sorted.filter((nn) => nn.start_sec >= t0 && nn.start_sec <= t1); |
There was a problem hiding this comment.
Bound the per-frame note window
Melody's Path calls windowSlice for every track on every animation frame, but this helper ignores its sorted precondition and scans/allocates over the entire track with filter. For long or dense MIDI files, rendering cost becomes O(total notes) at ~60Hz even though only a 10-second window is visible; use the sorted start times to binary-search/advance window bounds and only materialize visible notes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 420fce3. windowSlice now actually uses its sorted precondition: binary-searches the lower and upper bounds and returns slice(from, to) — O(log n + visible) per track per frame instead of filtering the entire track at 60Hz. Signature and inclusive [t0, t1] semantics unchanged; added an equivalence-vs-naive-filter test on a 500-note array plus boundary/empty-window cases (vitest 5/5).
…y-search windowSlice 1. VizCanvas: on the first frame after a viz/song switch AND on any time discontinuity (backward seek, or >1s forward jump), position the note scheduler at the playhead WITHOUT firing events, instead of replaying the whole skipped span as an onNoteOn flood (hundreds of blooms/orbs in one frame on dense MIDI). Also cures the pre-existing backward-seek replay. 2. geometry: windowSlice now honors its sorted precondition — binary-searches both bounds and slices, O(log n + visible) per track per frame instead of filtering every note at 60Hz. Same signature + inclusive semantics; added equivalence and boundary tests (5/5 passing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Triage of the Socket report: informational only, no action needed — the single added dependency is |
Adds a switchable visualization gallery and the first structure-revealing visualization, Melody's Path — a scrolling per-track melodic contour. Closes #2, closes #3.
What's in it
Visualizationwith optionalloadTimeline(notes, durationSec)so structure-revealing vises see the whole song (not just live events). cosmic-aurora ignores it.registry.ts+VizPicker.svelte(mirrors ThemePicker) + persistedvizsetting (restored on startup).VizCanvasrewritten to a single(vizId, midi)-driven effect that rebuilds the viz + RAF loop on switch/song-change —headis read only inside the loop, so there's no per-frame rebuild.pitchToY,windowSlice,leapIntensity).Verification
npm run check(0 errors),npm test(3/3),npm run build(clean). Frontend-only — no backend/Rust changes.Spec:
docs/superpowers/specs/2026-06-27-viz-gallery-melody-path-design.md· Plan:docs/superpowers/plans/2026-06-27-viz-gallery-melody-path.md🤖 Generated with Claude Code