A WebGL particle sphere that reacts to audio. Drop it into any DOM element — it handles everything else.
Inspired by the NCS-style visualizers with the neon glow and bass drops.
npm install glsl-audio-visualizer threethree is a peer dependency — you need it installed separately.
import { GLSLAudioVisualizer } from 'glsl-audio-visualizer';
const viz = new GLSLAudioVisualizer(document.getElementById('container')!);
// Load an audio file
fileInput.addEventListener('change', (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) viz.loadAudio(file);
});
// Clean up when done
viz.dispose();The container must have explicit dimensions (width + height). The canvas fills it 100%.
new GLSLAudioVisualizer(container: HTMLElement, options?: DeepPartial<VisualizerConfig>)All config options are optional — defaults are applied for anything you don't specify:
const viz = new GLSLAudioVisualizer(el, {
color: {
mode: 'gradient',
colors: ['#ff0000', '#00aaff', '#00ff88'],
},
sphere: { baseRadius: 1.2 },
bloom: { strength: 0.4 },
});The config is split into groups matching the visual sections.
| Property | Type | Default | Description |
|---|---|---|---|
mode |
ColorMode |
'audio' |
How colors are applied (see modes below) |
colors |
string[] |
['#fa0000','#0066ff'] |
Color palette — 1 to 16 hex colors |
hueSpeed |
number |
0.05 |
Rotation speed for hue mode |
Color modes:
solid— uses the first colorgradient— blends across the color array by vertical positionaudio— blends across the color array by energy levelhue— rainbow hue rotation, uses first color as base
| Property | Type | Default | Description |
|---|---|---|---|
baseRadius |
number |
1.4 |
Base sphere size |
bassScale |
number |
0.5 |
How much bass expands the radius |
feather |
number |
0.45 |
Softness of the sphere edge |
| Property | Type | Default | Description |
|---|---|---|---|
amplitude |
number |
0.35 |
Maximum particle displacement |
noiseScale |
number |
2.2 |
Scale of the noise pattern |
flowSpeed |
number |
0.15 |
Animation speed of the noise |
| Property | Type | Default | Description |
|---|---|---|---|
size |
number |
5.0 |
Base particle size in pixels |
opacity |
number |
0.4 |
Particle opacity (additive blending, keep low) |
softness |
number |
0.12 |
Edge softness of each particle |
sparkle |
number |
1.5 |
How much energy adds size variance |
variance |
number |
0.25 |
Noise-based color variance per particle |
| Property | Type | Default | Description |
|---|---|---|---|
strength |
number |
0.15 |
Bloom/glow intensity |
radius |
number |
0.85 |
Bloom spread radius |
threshold |
number |
0.0 |
Minimum brightness to bloom |
| Property | Type | Default | Description |
|---|---|---|---|
attackSpeed |
number |
0.3 |
How fast the sphere reacts to beats (0–1) |
releaseSpeed |
number |
0.05 |
How fast it decays after a beat (0–1) |
bassBoost |
number |
0.5 |
Bass frequency amplification |
energyBoost |
number |
1.0 |
Overall energy amplification |
viz.loadAudio(file: File): Promise<void>
viz.play(): void
viz.pause(): void
viz.togglePlay(): void
viz.seek(ratio: number): void // 0–1
viz.eject(): voidConnect any Web Audio API node — microphone, oscillator, media element, etc:
// Microphone
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const ctx = viz.getAudioContext();
const source = ctx.createMediaStreamSource(stream);
viz.connectNode(source);
// HTML <audio> element
const source = ctx.createMediaElementSource(audioEl);
viz.connectNode(source);// Subscribe to playback state changes
viz.onAudioStateChange = (state) => {
console.log(state.playing, state.progress, state.trackName);
console.log(state.currentTime, state.duration);
};
// Read current state
const state = viz.audioState;// Update any subset of config at runtime — other values are preserved
viz.setConfig({ bloom: { strength: 1.0 } });
viz.setConfig({ color: { mode: 'hue', colors: ['#ff00ff'] } });
// Read current config
const cfg = viz.getConfig();viz.dispose() // cancels RAF, releases GPU resources, removes canvasBackground is controlled via CSS on the container — not through the config. The canvas is always transparent:
#container {
background: #000; /* solid black */
background: transparent; /* see-through */
background: rgba(0,0,0,0.5); /* semi-transparent */
}This keeps the WebGL bloom pipeline clean and lets you composite the sphere over any content.
All config types are exported:
import type {
VisualizerConfig,
ColorConfig,
SphereConfig,
DisplacementConfig,
ParticlesConfig,
BloomConfig,
AudioConfig,
AudioState,
ColorMode,
DeepPartial,
} from 'glsl-audio-visualizer';The repo includes a Vue playground with a full settings panel:
npm install
npm run dev # start playground at localhost:5173
npm run build # build playground for GitHub Pages
npm run build:lib # build the library into dist/- 262,144 particles on a 512×512 grid, projected onto a sphere
- Simplex 3D noise (Ashima Arts) drives displacement — three swizzled axes for smooth 3D flow
- Bass expands the sphere radius with a quadratic response curve (weak bass barely registers, strong beats punch hard)
- Energy drives displacement amplitude — gated by bass so the sphere stays calm without beats
- UnrealBloomPass (Three.js post-processing) for the glow effect
- AdditiveBlending — works best on dark backgrounds
- Up to 16 colors interpolated across the palette based on mode
MIT