React

Props, callbacks & ref

The React component API — props, callback props, and the imperative handle.

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:

PropTypeDefaultDescription
showStatsbooleantrueShow the small connection/fps overlay in the corner
renderStats(state: { connected, bands, fps }) => ReactNodeReplace the overlay's content with your own (only rendered when showStats)
classNamestringExtra class names on the root element (the built-in fft-visualizer class is kept)
styleCSSPropertiesExtra styles on the root element (merged over the background option)

Callbacks

PropPayloadDescription
onConnectedData source became active
onDisconnectedData source stopped
onErrorstringError 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).

Copyright © 2026