Props, callbacks & ref
Props
The <FFTVisualizer> component accepts every shared option as a prop,
one-to-one, in camelCase:
<FFTVisualizer
mode="local"
bands={40}
gradient="sunset"
ledBars
smoothing={0.7}
/>
See Options for the full list of names, types, defaults, and meanings. Unset props keep the core's default, so you only pass what you want to change.
Custom gradient stops are compared by value, so an inline array is fine — the gradient
texture is only rebuilt when the colors actually change:
<FFTVisualizer
mode="local"
gradient={[
{ stop: 0, color: '#001233' },
{ stop: 1, color: 'hsl(280, 95%, 75%)' }
]}
/>
React-only props
Four props exist only on the React wrapper:
| Prop | Type | Default | Description |
|---|---|---|---|
showStats | boolean | true | Show the small connection/fps overlay in the corner |
renderStats | (state: { connected, bands, fps }) => ReactNode | — | Replace the overlay's content with your own (only rendered when showStats) |
className | string | — | Extra class names on the root element (the built-in fft-visualizer class is kept) |
style | CSSProperties | — | Extra styles on the root element (merged over the background option) |
Callbacks
| Prop | Payload | Description |
|---|---|---|
onConnected | — | Data source became active |
onDisconnected | — | Data source stopped |
onError | string | Error message (WS error, capture failure, WebGL init failure) |
onFrame | (data, left, right) | The display bar magnitudes, once per processed audio frame |
Callbacks are read through a ref, so inline arrow functions are fine — changing one never resubscribes or restarts the visualizer.
onFrame
Fires once per processed audio frame with the same 0–255 bar values the shader draws — a ready-made feed for external hardware (LED strips, flip-dot displays, …) without re-implementing any FFT. Listener-gated, so it costs nothing when unused.
// data: Uint8Array — one 0–255 magnitude per bar (length = `bands`)
// left / right: per-channel arrays in stereo mode, else null
// (in stereo, `data` is the per-bar max of both channels)
function onFrame(data: Uint8Array, left: Uint8Array | null, right: Uint8Array | null) {
const rows = 14 // e.g. dots per column on a flip-dot panel
data.forEach((v, col) => {
const lit = Math.round((v / 255) * rows)
// light dots 0..lit-1 in column `col`
})
}
<FFTVisualizer mode="local" bands={28} onFrame={onFrame} />
Imperative handle
Attach a ref for manual control:
import { useRef } from 'react'
import { FFTVisualizer, type FFTVisualizerHandle } from '@fft-visualizer/react'
const viz = useRef<FFTVisualizerHandle>(null)
// Manual connection control
viz.current?.connect()
viz.current?.disconnect()
viz.current?.isConnected // boolean
// Feed FFT frames imperatively (mode="external") — copies the data, so it works
// even when you reuse one buffer each frame, and avoids a render per frame
viz.current?.feedData(mono) // Uint8Array
viz.current?.feedData(mono, left, right) // stereo
// Local-audio device management
await viz.current?.getAudioDevices() // Promise<AudioDevice[]> (prompts for mic permission)
viz.current?.audioDevices // AudioDevice[]
viz.current?.activeAudioDeviceId // string | undefined
<FFTVisualizer ref={viz} mode="local" />
Standalone audio engines
There are no React-specific hooks: the data pipelines are framework-agnostic and live in the
core package. Use createLocalAudio / createWebSocketFft directly —
with mode="external", or to drive your own rendering. Both are callback-driven, which keeps
per-frame data out of React state.
import { createLocalAudio, createWebSocketFft } from '@fft-visualizer/core'
The raw WASM FFT processor is also re-exported from @fft-visualizer/react/wasm
(from @fft-visualizer/core/wasm).