npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fft-visualizer/react

v0.5.0

Published

A high-performance, WebGL-based real-time audio spectrum analyzer and FFT visualizer component for React — microphone, tab/system audio, WebSocket, or your own Web Audio data

Downloads

0

Readme

@fft-visualizer/react

A high-performance, WebGL-based real-time audio spectrum analyzer and FFT visualizer component for React — visualize the microphone, tab/system audio, a WebSocket stream, or your own Web Audio data.

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.

🔗 Live demo: https://demo.fftvisualizer.com · Docs: https://fftvisualizer.com

Highlights

  • WebGL fragment-shader rendering — the whole spectrum is one draw call; no per-bar canvas ops
  • Three data sources — capture audio locally (mic or tab/system), stream pre-computed FFT over WebSocket, or feed your own data via props
  • In-browser FFT — optional Rust/WASM FFT processor (lazy-loaded only when you capture audio locally)
  • Rich visual modes — LED segments (two styles), radial/circular, stereo, mirrored reflection, glow, rotation, per-level or per-axis coloring
  • Flexible gradients — 10 built-in presets or custom stops in any CSS color format
  • One peer dependency — just React; rendering uses native WebGL
  • SSR-safe — every browser API is touched inside useEffect, so server rendering emits markup only

Installation

pnpm add @fft-visualizer/react
# or: npm install @fft-visualizer/react / yarn add @fft-visualizer/react

Import the stylesheet once, anywhere in your app (it sizes the canvas and styles the stats overlay):

import '@fft-visualizer/react/style.css'

Quick start

The fastest way to see something is local mode — capture the microphone and visualize it entirely in the browser, no backend required:

import { FFTVisualizer } from '@fft-visualizer/react'
import '@fft-visualizer/react/style.css'

export function Viz() {
  // The component fills its container — give it a height
  return (
    <div style={{ width: '100%', height: 240 }}>
      <FFTVisualizer mode="local" bands={40} gradient="aurora" />
    </div>
  )
}

The browser will prompt for microphone permission on mount. To capture tab or system audio instead of the mic, set audioSource="display".

Next.js / React Server Components

The component renders on the server, but it needs the browser to do anything, so it belongs in a client component:

'use client'

import { FFTVisualizer } from '@fft-visualizer/react'

Data modes

The component has one job — render a spectrum — and three ways to get the data, selected with the mode prop:

| 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 websocketUrl and reads pre-computed FFT frames | A server/device already produces FFT data (e.g. a Raspberry Pi) | | external | You pass FFT magnitudes via the data / dataLeft / dataRight props | You have your own audio pipeline (Web Audio, another analyser, etc.) |

Local mode

<FFTVisualizer mode="local" audioSource="mic" bands={80} />

WebSocket mode

<FFTVisualizer mode="websocket" websocketUrl="ws://localhost:3001/fft" bands={40} />

The server streams a small config message then binary FFT frames — see WebSocket protocol. Reference servers for Python, Node.js and Rust are in backend-examples/.

External mode

Pass a Uint8Array of magnitudes (0–255). For stereo, pass dataLeft and dataRight instead of data.

<FFTVisualizer mode="external" data={data} bands={40} />

Note: the data prop is compared by reference, so mutating the same Uint8Array in place won't be picked up. Either set a fresh array in state, or — better for per-frame updates — call feedData() on the ref (below), which copies the data and skips a React render per frame.

Props

Props are the core visualizer's options, so anything valid for @fft-visualizer/core works here. Unset props keep the core's default.

Data source

| Prop | Type | Default | Description | |------|------|---------|-------------| | mode | 'websocket' \| 'local' \| 'external' | 'websocket' | Where FFT data comes from | | websocketUrl | string | — | WebSocket URL (used when mode="websocket") | | data | Uint8Array | — | External FFT magnitudes, mono (used when mode="external") | | dataLeft / dataRight | Uint8Array | — | External FFT magnitudes per channel (stereo external mode) | | audioSource | 'mic' \| 'display' | 'mic' | Local capture source (used when mode="local") | | audioDeviceId | string | — | Specific input device for local mic capture | | autoReconnect | boolean | false | Reconnect the WebSocket with exponential backoff (1s→30s) after an unexpected drop |

Data processing

| Prop | Type | Default | Description | |------|------|---------|-------------| | bands | 10 \| 20 \| 40 \| 80 | 80 | Number of frequency bands displayed (server bins are aggregated down to this) | | noiseFloor | number | 0 | Cut magnitudes below this threshold (0–255) | | smoothing | number | 0 | Temporal smoothing (0 = none, 0.9 = heavy) | | showPeaks | boolean | true | Show falling peak indicators | | peakDecay | number | 0.997 | Peak fall speed (0.99 = slow, 0.9 = fast) | | stereo | boolean | false | Stereo mode: left channel top, right channel bottom (mono data is mirrored to both) |

Appearance

| Prop | Type | Default | Description | |------|------|---------|-------------| | ledBars | boolean | false | LED segment effect | | ledShape | 'segment' \| 'meter' | 'segment' | segment = fixed-pixel gap lines (identical at every resolution); meter = short, wide segments sized from bar width, like a classic LED meter | | lumiBars | boolean | false | Full-height bars whose brightness follows the level | | radial | boolean | false | Circular spectrum: angle = frequency, radius = level | | radialInnerRadius | number | 0.35 | Radial: inner hole radius as a fraction of outer radius (0–0.9) | | barSpace | number | 0.25 | Gap between bars as a fraction of bar width (0–0.9) | | reflexRatio | number | 0 | Mirrored reflection (0 = off; max 0.7). Linear mono: fraction of the canvas height given to the reflection. Radial: how far the mirror reaches inward, as a fraction of the bars’ radial length | | reflexAlpha | number | 0.25 | Reflection brightness (0–1) | | glow | number | 0 | Glow above the bar tops (0 = off, 1 = max) | | rotation | 0 \| 90 \| 180 \| 270 | 0 | Rotate the whole visual clockwise, in degrees | | gradient | GradientName \| GradientStop[] | 'classic' | Bar colors: a preset name or custom stops | | gradientDirection | 'vertical' \| 'horizontal' | 'vertical' | Gradient axis | | colorMode | 'gradient' \| 'bar-level' | 'gradient' | bar-level colors each whole bar by its current level instead of by the gradient axis | | background | string | '#0a0a0a' | Background behind and between the bars. Any CSS color, including 'transparent' or rgba(…) for a see-through canvas that blends into your page (opaque vs. transparent is fixed at mount) | | debug | boolean | false | Log connection/config diagnostics to the console (quiet by default) |

React-only props

| 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 | | style | CSSProperties | — | Extra styles on the root element |

Gradients

Bar colors are rasterized into a 256×1 lookup texture, so any CSS color and native gradient interpolation work. Pass a preset name or an array of custom stops:

// Preset
<FFTVisualizer mode="local" gradient="sunset" />

// Custom stops (any CSS color format)
<FFTVisualizer
  mode="local"
  gradient={[
    { stop: 0, color: '#001233' },
    { stop: 0.5, color: 'rgb(15, 155, 142)' },
    { stop: 1, color: 'hsl(280, 95%, 75%)' }
  ]}
/>

Custom stops are compared by value, so an inline array like the one above is fine — the gradient texture is only rebuilt when the colors actually change.

Built-in presets (exported as gradientPresets / gradientNames): classic, rainbow, blue, prism, orangered, steelblue, sunset, aurora, dusk, mono.

Gradient helpers are exported for building settings UIs or your own rendering: resolveGradientStops, buildGradientLUT, and the GradientStop / GradientName / GradientInput types.

Callbacks

| Prop | Payload | Description | |------|---------|-------------| | onConnected | — | Data source became active (WS connected / capture started / external started) | | onDisconnected | — | Data source stopped | | onError | string | Error message (WS error, capture failure, WebGL init failure) | | onFrame | (data, left, right) | Called once per processed audio frame with the display bar magnitudes (see below) |

Callbacks are read from 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 bar values the shader draws — useful for driving external hardware (LED strips, flip-dot displays, …) without re-implementing the FFT. It's 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

The data pipelines are framework-agnostic and live in the core package — use them directly (with mode="external", or to drive your own rendering):

import { createLocalAudio, createWebSocketFft } from '@fft-visualizer/core'

createLocalAudio captures mic/display audio and runs the WASM FFT; createWebSocketFft computes the FFT in-browser from a raw PCM WebSocket stream. Both are callback-driven, which keeps per-frame data out of React state. The raw WASM processor is also re-exported from @fft-visualizer/react/wasm.

WebSocket protocol

mode="websocket" expects a server that sends pre-computed FFT frames: one JSON config message on connect, then a binary uint8 (0–255) per frequency bin, bins bytes per frame.

{ "type": "config", "mode": "fft", "bins": 80, "fps": 120 }

The full spec and reference servers (Python, Node.js, Rust) are in the repository and on the docs site.

Related packages

| Package | For | |---------|-----| | @fft-visualizer/core | Vanilla JS/TS, or any other framework | | @fft-visualizer/vue | Vue 3 |

License

MIT © Wouter Vernaillen