Skip to content

feat: add radial FFT visualizer - #35

Merged
lstebner merged 3 commits into
mainfrom
feat/visualizer
Jul 12, 2026
Merged

feat: add radial FFT visualizer#35
lstebner merged 3 commits into
mainfrom
feat/visualizer

Conversation

@lstebner

@lstebner lstebner commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a full-screen visualizer panel opened via a Waves icon in the toolbar (Escape or icon toggle to close)
  • Rust audio pipeline gains a new AnalysisSource wrapper (replacing RmsSource) that computes a 2048-sample Hann-windowed FFT via rustfft on every decoded frame, maps output into 64 log-spaced frequency bands with attack/decay smoothing, and writes both RMS and band magnitudes to lock-free shared atomics
  • Engine tick rate lowered from 250ms to 50ms; a visualizer-data event fires at ~20Hz only while the panel is open, gated by playback_enable_visualizer / playback_disable_visualizer commands to avoid overhead when closed
  • Frontend canvas requestAnimationFrame loop interpolates toward incoming FFT bands between events for smooth motion; bars are drawn as radial wedge arcs with a glow gradient in the current theme's accent colour
  • Album art fades to 28% opacity and morphs to a circle (0.5s transition) while playing — fitting inside the bar ring like a record centre — then returns to full opacity and rounded-rect on pause (1.5s fade)

Test plan

  • Open visualizer while a track is playing — radial bars animate reactively
  • Pause/resume — art fades and morphs between circle and square
  • Close with Escape key and with the toolbar button
  • Open and close rapidly — no orphaned event listeners or missing enable/disable calls
  • Resize window — bars stay within bounds, no clipping at top
  • Open visualizer with nothing playing — panel shows placeholder art, bars are flat
  • cargo test --lib passes (117 tests including new FFT/RMS analysis tests)
  • npm run check and npm run test pass
Screencast.from.2026-07-11.22-01-19.mp4
Screencast.from.2026-07-12.09-08-05.mp4

closes #8

🤖 Generated with Claude Code

lstebner and others added 3 commits July 11, 2026 21:35
Adds a full-screen visualizer panel (Waves icon in toolbar, Escape to
close) with 64-band FFT-driven radial bars around the album art.

- Rust: new `AnalysisSource` wraps the audio pipeline with RMS EMA and
  a 2048-sample Hann-windowed FFT (rustfft), writing log-spaced band
  magnitudes with attack/decay smoothing to a shared ArcSwap. Engine
  tick lowered to 50ms; a new `visualizer-data` event fires at 20Hz
  only while the panel is open (enable/disable commands gate it).
- Frontend: canvas rAF loop interpolates toward incoming FFT bands for
  smooth motion between frames; radial wedge bars drawn with a glow
  gradient from the accent colour. Album art fades to 28% opacity and
  morphs to a circle while playing, returning on pause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gnette

- Replace clearRect with an annulus-clipped decay fill so bars ghost out
  with a brief motion trail; the clip confines the fill to the bar ring so
  the center (behind the art) and canvas exterior stay transparent
- Parse the active theme's --accent hex into HSL and shift hue ±25° across
  band index for each bar, keeping saturation and lightness from the theme
  so every palette stays recognizable
- Switch the full-panel scrim from a uniform overlay to a horizontal gradient
  (darker at edges, lighter in center) so the blurred album art background
  glows through in the middle
- Fix event-listener leak: store and call the unlisten fn from onVisualizerData
  in onDestroy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ering

- Add an "Outline" style that draws a smooth closed curve through the tip
  positions of the 64 bars using midpoint quadratic Bezier segments, giving
  a C1-continuous polar waveform with per-segment frequency-mapped hue
- Outline uses only bands 6–57 (~40Hz–10kHz) spread evenly around the full
  circle, skipping the sub-bass and ultra-high bands that are typically silent
  and caused a visible flat gap where the curve closed at 12 o'clock
- Outline decays at alpha 0.05 vs bars' 0.15 so ghost shapes accumulate into
  visible artifacts before clearing
- Add a Dropdown in the top-left corner to switch between Bars and Outline;
  Escape inside the dropdown closes the list without closing the visualizer
- Persist the selected style to localStorage (key: spudbox.visualizerStyle)
  so it survives closing and reopening the visualizer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lstebner
lstebner merged commit 62e1d2f into main Jul 12, 2026
2 checks passed
@lstebner
lstebner deleted the feat/visualizer branch July 12, 2026 16:39
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.

add visualizer

1 participant