audio-ambient-light
v1.0.0
Published
Audio-reactive ambient light visualization library for Web Audio API
Maintainers
Readme
🎵 Audio Ambient Light
Audio-reactive ambient light visualization library for Web Audio API. Create beautiful, responsive light effects that dance to your music.
✨ Features
- 🎵 Audio Processing Core - Converts frequency data to energy values (0-1)
- 🌉 Bridge Pattern - Manages data flow between audio source and visualization
- 🎨 DOM Renderer - Smooth animations with configurable appearance
- 🌈 Gradient Presets - Built-in beautiful gradient color themes
- 🎭 Custom Gradients - Define your own color arrays for unique effects
- 📡 Event System - Subscribe to peaks, silence, and update events
- ⚡ Zero Dependencies - Lightweight and framework-agnostic
- 🚀 High Performance - Optimized processing and CSS-variable-based rendering
- 📦 Multiple Formats - ESM, CJS, and IIFE (browser) builds
📦 Installation
# npm
npm install audio-ambient-light
# pnpm
pnpm add audio-ambient-light
# yarn
yarn add audio-ambient-light🚀 Quick Start
Browser (CDN/IIFE)
<div id="visualizer" style="width: 80px; height: 400px;"></div>
<script src="https://unpkg.com/audio-ambient-light/dist/index.global.js"></script>
<script>
const renderer = new AmbientLight.AmbientLightRenderer({
container: document.getElementById('visualizer'),
appearance: {
color: 'gradient-blue-purple',
gradient: true,
direction: 'up',
},
});
renderer.mount();
// Update with audio data (0-1 range)
renderer.update(0.75);
</script>ES Module
import {
AmbientLightCore,
AmbientLightBridge,
AmbientLightRenderer,
} from 'audio-ambient-light';
// 1. Create renderer
const renderer = new AmbientLightRenderer({
container: document.getElementById('visualizer')!,
appearance: {
color: 'gradient-rainbow',
gradient: true,
},
});
renderer.mount();
// 2. Create core processor
const core = new AmbientLightCore();
// 3. Set up audio analyser (Web Audio API)
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
const frequencyData = new Uint8Array(analyser.frequencyBinCount);
// 4. Animation loop
function animate() {
analyser.getByteFrequencyData(frequencyData);
const energy = core.process(frequencyData);
if (energy !== null) {
renderer.update(energy);
}
requestAnimationFrame(animate);
}
animate();With Audio File
// Load and play audio file
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const response = await fetch('your-audio.mp3');
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(analyser);
analyser.connect(audioContext.destination);
source.start(0);
// Now use the analyser with AmbientLightCore📖 API Reference
AmbientLightCore
The core audio processing engine.
const core = new AmbientLightCore(config?: Partial<AmbientLightConfig>);
// Process frequency data → returns 0-1 energy value
const energy = core.process(frequencyData: Uint8Array);
// Update configuration
core.setConfig({ minDecibels: -90, bassWeight: 1.2 });
// Get current config
const config = core.getConfig();
// Reset internal state
core.reset();
// Subscribe to events
core.on('peak', ({ value }) => console.log('Peak detected:', value));
core.on('silence', ({ duration }) => console.log('Silence duration:', duration));
core.on('update', ({ value }) => console.log('Energy updated:', value));AmbientLightRenderer
DOM-based renderer with smooth animations.
const renderer = new AmbientLightRenderer({
container: HTMLElement,
side?: 'left' | 'right',
appearance?: {
color: string, // RGB string or 'gradient-{preset}'
opacity: number, // 0-1
shape: 'rounded' | 'rectangle' | 'pill',
gradient: boolean, // Enable top fade effect
border: boolean, // Enable border
direction: 'up' | 'center',
width: number | string, // Width in % or px (default: 50)
customGradient?: string[], // Array of RGB strings
},
animationConfig?: {
lerpUp: number, // Rise speed (0-1)
lerpDown: number, // Fall speed (0-1)
minDiff: number, // Min change threshold (default: 2)
},
});
renderer.mount(); // Mount to container
renderer.update(energy); // Update with 0-1 value
renderer.updateAppearance({ color: 'gradient-sunset' });
renderer.updateAnimationConfig({ lerpDown: 0.15 });
// Subscribe to events
renderer.on('update', ({ height }) => console.log('Visual height:', height));
renderer.on('mounted', () => console.log('Renderer mounted'));
renderer.unmount(); // CleanupAmbientLightBridge
Manages the processing loop automatically.
const bridge = new AmbientLightBridge({
dataProvider: {
getFrequencyData: () => Uint8Array | null,
setAnalyserConfig?: (config) => void,
},
onData: (value: number) => void,
onConfig?: (config: AnimationConfig) => void,
config?: Partial<AmbientLightConfig>,
});
bridge.start(); // Start processing loop
bridge.stop(); // Stop processing loop
bridge.updateConfig({ bassWeight: 1.5 });🎨 Gradient Presets
| Preset | Colors |
|--------|--------|
| gradient-blue-purple | Blue → Purple |
| gradient-rainbow | Green → Yellow → Red |
| gradient-sunset | Pink → Orange-Pink |
| gradient-flame | Magenta → Orange |
| gradient-ocean | Deep Blue → Cyan |
| gradient-forest | Forest Green → Light Green |
import { GRADIENT_PRESETS, isGradientPreset } from 'audio-ambient-light';
// Check if color is a preset
isGradientPreset('gradient-blue-purple'); // true
// Access preset config
console.log(GRADIENT_PRESETS['blue-purple']);
// { colors: ['59, 130, 246', '168, 85, 247'], glowColor: '168, 85, 247' }📡 Event System
Both Core and Renderer implement a simple event emitter for integration.
Core Events
update: Emitted on every energy update.peak: Emitted when energy exceeds the peak threshold.silence: Emitted when no significant audio is detected.
Renderer Events
update: Emitted when the visual height changes.mounted: Emitted after the DOM elements are added.unmounted: Emitted after removal.
import { SimpleEventEmitter } from 'audio-ambient-light';
const emitter = new SimpleEventEmitter();
emitter.on('some-event', (data) => { ... });⚡ Performance Optimization
- CSS Variables: Styles are applied via CSS variables to minimize DOM style property writes and layout thrashing.
- TypedArrays: Audio processing uses
Uint8Arraydirectly without unnecessary slicing or object creation. - Single Pass: RMS and Peak values are calculated in a single loop over frequency data.
- Batched Updates: Rendering updates are throttled using a configurable
minDiffthreshold.
⚙️ Configuration
AmbientLightConfig
interface AmbientLightConfig {
// Sensitivity
minDecibels: number; // -100 to 0, filter weak sounds
maxDecibels: number; // -100 to 0
minThreshold: number; // 0-1, values below become zero
// Visual
curveExponent: number; // 1-3, contrast control
lerpUp: number; // 0-1, rise speed
lerpDown: number; // 0-1, fall speed
minDiff: number; // 0-100, min energy change to emit (Core)
// ... (Renderer minDiff is set in animationConfig, default 2)
// Smoothing
smallChangeThreshold: number;
smallChangeSmoothing: number;
// Peak Detection
rmsWeight: number; // RMS contribution
peakWeight: number; // Peak contribution
// Spectrum
fftSize: number; // 32, 64, 128...
smoothingTimeConstant: number;
bassEndRatio: number; // Bass frequency range
midEndRatio: number; // Mid frequency range
bassWeight: number; // Bass importance
midWeight: number; // Mid importance
// Normalization
peaksHistoryLength: number;
minReferencePeak: number;
}📊 Output
The core outputs a normalized energy value from 0 to 1:
0.0 → Silent / No audio
0.5 → Medium energy
1.0 → Maximum energy (peak)The renderer converts this to visual height (0-95% of container).
🔗 Examples
Run the interactive demo:
git clone https://github.com/flystar233/ambient-light.git
cd ambient-light
npm install
npm run build
npm run exampleThen open http://localhost:3000/examples/standalone.html
📄 License
MIT © xfly
