--- id: Bbeierle12/Skill-MCP-Claude/audio-reactive version: "89c30102" license: MIT install: manual updated: 2026-07-27 --- # audio-reactive — audio-reactive lets you create dynamic visual experiences synchronized to sound. Tap into music frequencies and beat detection to drive animations, lighting, and effects that pulse with your audio. Perfect for live performances, interactive installations, and immersive media projects. Publisher: Bbeierle12 · Stars: 8 · Updated: 2026-07-27 Install (manual): `git clone https://github.com/Bbeierle12/Skill-MCP-Claude` ## SKILL.md # Audio Reactive Visuals Connecting audio analysis to visual parameters. ## Quick Start ```javascript import * as Tone from 'tone'; const analyser = new Tone.Analyser('fft', 256); const player = new Tone.Player('/audio/music.mp3'); player.connect(analyser); player.toDestination(); function animate() { const fft = analyser.getValue(); const bass = (fft[2] + 100) / 100; // Normalize to 0-1 // Apply to visual element.style.transform = `scale(${1 + bass * 0.2})`; requestAnimationFrame(animate); } ``` ## Core Patterns ### Audio-to-Visual Mapping ```javascript class AudioReactiveMapper { constructor(analyser) { this.analyser = analyser; this.smoothers = { bass: new Smoother(0.85), mid: new Smoother(0.9), high: new Smoother(0.92) }; } // Get values optimized for different visual properties getValues() { const fft = this.analyser.getValue(); const len = fft.length; // Extract and normalize bands const rawBass = this.avgRange(fft, 0, len * 0.1); const rawMid = this.avgRange(fft, len * 0.1, len * 0.5); const rawHigh = this.avgRange(fft, len * 0.5, len); return { bass: this.smoothers.bass.update(this.normalize(rawBass)), mid: this.smoothers.mid.update(this.normalize(rawMid)), high: this.smoothers.high.update(this.normalize(rawHigh)) }; } avgRange(data, start, end) { let sum = 0; for (let i = Math.floor(start); i < Math.floor(end); i++) { sum += data[i]; } return sum / (end - start); } normalize(db) { return Math.max(0, Math.min(1, (db + 100) / 100)); } } class Smoother { constructor(factor = 0.9) { this.factor = factor; this.value = 0; } update(input) { this.value = this.factor * this.value + (1 - this.factor) * input; return this.value; } } ``` ## Visual Property Mappings ### Scale/Size ```javascript // Pulse on bass function updateScale(element, bass) { const scale = 1 + bass * 0.3; // 1.0 to 1.3 element.style.transform = `scale(${scale})`; } // Three.js mesh function updateMeshScale(mesh, bass) { const scale = 1 + bass * 0.5; mesh.scale.setScalar(scale); } ``` ### Position/Movement ```javascript // Vibration based on high frequencies function updatePosition(element, high) { const shake = high * 5; // Pixels const x = (Math.random() - 0.5) * shake; const y = (Math.random() - 0.5) * shake; element.style.transform = `translate(${x}px, ${y}px)`; } // Smooth wave motion function updateWavePosition(mesh, mid, time) { mesh.position.y = Math.sin(time * 2) * mid * 2; } ``` ### Rotation ```javascript // Continuous rotation speed based on energy class SpinController { constructor() { this.rotation = 0; } update(energy) { const speed = 0.01 + energy * 0.05; this.rotation += speed; return this.rotation; } } ``` ### Color/Brightness ```javascript // Brightness based on volume function updateBrightness(element, volume) { const brightness = 0.5 + volume * 0.5; // 50% to 100% element.style.filter = `brightness(${brightness})`; } // Color shift based on frequency balance function getReactiveColor(bass, mid, high) { // More bass = more red/magenta, more high = more cyan const r = Math.floor(bass * 255); const g = Math.floor(mid * 100); const b = Math.floor(high * 255); return `rgb(${r}, ${g}, ${b})`; } // Three.js emissive intensity function updateGlow(material, bass) { material.emissiveIntensity = 1 + bass * 3; } ``` ### Opacity/Visibility ```javascript // Fade based on volume function updateOpacity(element, volume) { element.style.opacity = 0.3 + volume * 0.7; } ``` ## Beat Response ### Simple Beat Flash ```javascript class BeatFlash { constructor(decayRate = 0.95) { this.intensity = 0; this.decayRate = decayRate; } trigger() { this.intensity = 1; } update() { this.intensity *= this.decayRate; return this.intensity; } } // Usage const flash = new BeatFlash(); function animate() { if (beatDetector.detect(analyser)) { flash.trigger(); } const intensity = flash.update(); element.style.backgroundColor = `rgba(0, 245, 255, ${intensity})`; requestAnimationFrame(animate); } ``` ### Beat Scale Pop ```javascript class BeatPop { constructor(popScale = 1.3, decayRate = 0.9) { this.scale = 1; this.targetScale = 1; this.popScale = popScale; this.decayRate = decayRate; } trigger() { this.targetScale = this.popScale; } update() { // Lerp toward target, then decay target back to 1 this.scale += (this.targetScale - this.scale) * 0.3; this.targetScale = 1 + (this.targetScale - 1) * this.decayRate; return this.scale; } } ``` ### Beat Spawn ```javascript class BeatSpawner { constructor(onSpawn) { this.onSpawn = onSpawn; this.cooldown = 0; this.minInterval = 200; // ms } check(isBeat) { if (isBeat && this.cooldown <= 0) { this.onSpawn(); this.cooldown = this.minInterval; } this.cooldown -= 16; // Assuming 60fps } } // Usage: spawn particles on beat const spawner = new BeatSpawner(() => { particleSystem.emit(10); }); ``` ## React Three Fiber Integration ### Audio Reactive Component ```tsx import { useRef } from 'react'; import { useFrame } from '@react-three/fiber'; function AudioReactiveSphere({ audioData }) { const meshRef = useRef(); const materialRef = useRef(); useFrame(() => { if (!audioData || !meshRef.current) return; const { bass, mid, high, isBeat } = audioData; // Scale on bass const scale = 1 + bass * 0.5; meshRef.current.scale.setScalar(scale); // Rotation speed on mid meshRef.current.rotation.y += 0.01 + mid * 0.03; // Emissive on high if (materialRef.current) { materialRef.current.emissiveIntensity = 1 + high * 2; } }); return ( ); } ``` ### Audio Context Hook ```tsx function useAudioAnalysis(playerRef) { const [data, setData] = useState(null); const analyserRef = useRef(null); const mapperRef = useRef(null); useEffect(() => { analyserRef.current = new Tone.Analyser('fft', 256); mapperRef.current = new AudioReactiveMapper(analyserRef.current); if (playerRef.current) { playerRef.current.connect(analyserRef.current); } return () => analyserRef.current?.dispose(); }, []); useFrame(() => { if (mapperRef.current) { setData(mapperRef.current.getValues()); } }); return data; } ``` ## Post-Processing Integration ### Audio-Reactive Bloom ```tsx function AudioReactiveBloom({ audioData }) { const bloomRef = useRef(); useFrame(() => { if (bloomRef.current && audioData) { // Bass drives bloom intensity bloomRef.current.intensity = 1 + audioData.bass * 2; // High frequencies lower threshold (more bloom) bloomRef.current.luminanceThreshold = 0.3 - audioData.high * 0.2; } }); return ( ); } ``` ### Audio-Reactive Chromatic Aberration ```tsx function AudioReactiveChroma({ audioData }) { const chromaRef = useRef(); useFrame(() => { if (chromaRef.current && audioData) { // High frequencies drive aberration const offset = 0.001 + audioData.high * 0.005; chromaRef.current.offset.set(offset, offset * 0.5); } }); return ; } ``` ## GSAP Integration ### Audio-Driven Timeline ```javascript class AudioTimeline { constructor() { this.timeline = gsap.timeline({ paused: true }); this.progress = 0; } build(duration) { this.timeline .to('.element', { scale: 1.5 }, 0) .to('.element', { rotation: 360 }, duration * 0.5) .to('.element', { scale: 1 }, duration); } updateWithAudio(bass) { // Map bass to timeline progress const targetProgress = this.progress + bass * 0.02; this.progress = Math.min(1, targetProgress); this.timeline.progress(this.progress); } } ``` ### Beat-Triggered GSAP ```javascript function createBeatAnimation(element) { return gsap.timeline({ paused: true }) .to(element, { scale: 1.2, duration: 0.1, ease: 'power2.out' }) .to(element, { scale: 1, duration: 0.3, ease: 'power2.out' }); } // On beat if (isBeat) { beatAnimation.restart(); } ``` ## Complete System Example ```javascript class AudioVisualSystem { constructor(player, visualElements) { this.player = player; this.elements = visualElements; // Analysis this.analyser = new Tone.Analyser('fft', 256); this.mapper = new AudioReactiveMapper(this.analyser); this.beatDetector = new BeatDetector(); // Reactive controllers this.beatFlash = new BeatFlash(); this.beatPop = new BeatPop(); // Connect this.player.connect(this.analyser); this.player.toDestination(); } update() { const values = this.mapper.getValues(); const isBeat = this.beatDetector.detect(this.analyser); if (isBeat) { this.beatFlash.trigger(); this.beatPop.trigger(); } const flashIntensity = this.beatFlash.update(); const popScale = this.beatPop.update(); // Apply to visuals this.elements.background.style.opacity = 0.5 + values.bass * 0.5; this.elements.circle.style.transform = `scale(${popScale})`; this.elements.glow.style.boxShadow = `0 0 ${flashIntensity * 50}px rgba(0, 245, 255, ${flashIntensity})`; } start() { const animate = () => { this.update(); requestAnimationFrame(animate); }; animate(); } } ``` ## Temporal Collapse Integration ```javascript const TEMPORAL_MAPPINGS = { // Digit glow intensity digitGlow: (bass, mid) => 1 + bass * 2 + mid, // Particle emission rate particleRate: (bass, isBeat) => isBeat ? 50 : bass * 20, // Background pulse backgroundBrightness: (bass) => 1 + bass * 0.3, // Time dilation visual (warp effect) warpIntensity: (high) => high * 0.5, // Vignette darkness (close in on beat) vignetteDarkness: (isBeat, current) => isBeat ? 0.7 : current * 0.95, // Chromatic aberration (glitch on high) chromaticOffset: (high) => 0.001 + high * 0.004 }; function applyTemporalMappings(audioData, visuals) { const { bass, mid, high, isBeat } = audioData; visuals.digitMaterial.emissiveIntensity = TEMPORAL_MAPPINGS.digitGlow(bass, mid); visuals.particles.emissionRate = TEMPORAL_MAPPINGS.particleRate(bass, isBeat); visuals.background.brightness = TEMPORAL_MAPPINGS.backgroundBrightness(bass); visuals.bloom.intensity = 1.5 + bass * 1.5; visuals.chromatic.offset = TEMPORAL_MAPPINGS.chromaticOffset(high); } ``` ## Performance Tips ```javascript // 1. Update at lower frequency if possible let frameCount = 0; function animate() { if (frameCount % 2 === 0) { updateAudioData(); } applyVisuals(); frameCount++; } // 2. Use smoothing to hide skipped frames const smoother = new Smoother(0.95); // Higher = more smoothing // 3. Batch DOM updates function applyAllStyles(elements, values) { // Single reflow requestAnimationFrame(() => { elements.forEach((el, i) => { el.style.transform = `scale(${values[i]})`; }); }); } // 4. Use CSS variables for multiple elements document.documentElement.style.setProperty('--audio-bass', bass); // CSS: transform: scale(calc(1 + var(--audio-bass) * 0.2)); ``` ## Reference - See `audio-playback` for audio loading and transport - See `audio-analysis` for FFT and beat detection - See `audio-router` for audio domain routing [View on SkillFed](https://skillfed.io/Bbeierle12/Skill-MCP-Claude/audio-reactive) · [View on GitHub](https://github.com/Bbeierle12/Skill-MCP-Claude)