A high-performance, WebGL-based real-time audio spectrum analyzer and FFT visualizer. The entire visual — bars, LED segments, radial layout, gradient, glow, reflection — is drawn by a single fragment shader in one GPU draw call, so it stays smooth even at 120 fps with 80 bands in stereo.
This is a monorepo. The rendering engine lives in a framework-agnostic core package; framework integrations are thin wrappers around it.
| Package | Description | |
|---|---|---|
@fft-visualizer/core |
Framework-agnostic WebGL renderer + audio/WebSocket engines as a vanilla TypeScript class. Zero framework dependencies. | packages/core |
@fft-visualizer/vue |
Vue 3 component wrapping the core. One peer dependency: Vue 3. | packages/vue |
@fft-visualizer/react |
React component wrapping the core. One peer dependency: React 18/19. | packages/react |
The core owns all rendering, audio capture (mic / tab / system via the Web Audio API + a lazy-loaded Rust/WASM FFT), WebSocket streaming, and the WebGL draw. Each framework package is a thin adapter that maps its own reactivity/props onto the core's imperative API.
-
Using Vue? Install
@fft-visualizer/vue— see its README for props, modes, and examples. -
Using React? Install
@fft-visualizer/react— same options as props, plus callbacks and an imperative ref. -
Vanilla JS/TS, or another framework? Use
@fft-visualizer/coredirectly:import { FFTVisualizer } from '@fft-visualizer/core' const viz = new FFTVisualizer(canvas, { mode: 'local', bands: 40, gradient: 'aurora' }) // ...later viz.destroy()
The renderer has one job — draw a spectrum — and three ways to get the data:
| Mode | How data arrives | Use when |
|---|---|---|
local |
Captures mic or display audio and computes the FFT in-browser (Rust/WASM) | You want a zero-backend, client-side visualizer |
websocket (default) |
Connects to a URL and reads pre-computed FFT frames | A server/device already produces FFT data (e.g. a Raspberry Pi) |
external |
You feed FFT magnitudes in yourself | You have your own audio pipeline |
Reference servers that stream FFT frames over WebSocket (Python, Node.js, Rust)
are in backend-examples/.
packages/
core/ # @fft-visualizer/core — vanilla renderer + engines
src/ # FFTVisualizer class, localAudio, webSocketFft, gradients, processing
wasm/ # Rust FFT processor (wasm-pack, bundler target; pkg/ is committed)
vue/ # @fft-visualizer/vue — Vue 3 wrapper
src/ # FFTVisualizer.vue, composables
react/ # @fft-visualizer/react — React wrapper
src/ # FFTVisualizer.tsx
playground/ # the demo site (deployed to demo.fftvisualizer.com)
shared/ # presets, control descriptors, radio source, stylesheet
core/ vue/ # one app per package, all four at full parity…
react/ nuxt/ # …served at /core/, /vue/, /react/, /nuxt/
site/ # switcher landing + the nginx config the image runs
backend-examples/ # reference WebSocket FFT servers (python / nodejs / rust)
Every playground renders the same controls and presets from playground/shared,
so the four apps show the same visualizer through four different APIs rather than
four drifting demos.
Uses pnpm workspaces.
pnpm install # install all workspace deps
pnpm dev # run the Vue playground (dev:core / dev:react / dev:nuxt for the others)
pnpm build # build every package (pnpm -r build)
pnpm typecheck # typecheck every package
pnpm test # run all tests (core: node, vue/react: browser via Playwright)
pnpm lint:fix # ESLint with auto-fix
pnpm build:wasm # rebuild the Rust WASM FFT (requires the Rust toolchain + wasm-pack)
pnpm build:playground # build all four apps into playground/site/dist (what the image serves)
pnpm preview # serve that assembled site, radio proxy includedThe committed packages/core/wasm/pkg/ artifacts mean a normal build needs no
Rust toolchain — only pnpm build:wasm does.
MIT © Wouter Vernaillen