Skip to content

Repository files navigation

glsl-audio-visualizer

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.

Live demo


Install

npm install glsl-audio-visualizer three

three is a peer dependency — you need it installed separately.


Quick start

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%.


Constructor

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 },
});

Config

The config is split into groups matching the visual sections.

color

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 color
  • gradient — blends across the color array by vertical position
  • audio — blends across the color array by energy level
  • hue — rainbow hue rotation, uses first color as base

sphere

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

displacement

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

particles

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

bloom

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

audio

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

API

Audio

viz.loadAudio(file: File): Promise<void>
viz.play(): void
viz.pause(): void
viz.togglePlay(): void
viz.seek(ratio: number): void  // 0–1
viz.eject(): void

External audio source

Connect 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);

State

// 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;

Config

// 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();

Lifecycle

viz.dispose()  // cancels RAF, releases GPU resources, removes canvas

Background

Background 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.


TypeScript

All config types are exported:

import type {
  VisualizerConfig,
  ColorConfig,
  SphereConfig,
  DisplacementConfig,
  ParticlesConfig,
  BloomConfig,
  AudioConfig,
  AudioState,
  ColorMode,
  DeepPartial,
} from 'glsl-audio-visualizer';

Playground (dev)

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/

How it works

  • 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

License

MIT

About

demo project heavily inspired by those old NCS (No Copyright Sounds) visualizers — you know, the ones with the neon circles and bass drops.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages