🎵 Live Demo: AlaskaButter.com - Experience intelligent music visualization in your browser!
Butterchurn is an intelligent WebGL implementation of the Milkdrop Visualizer with advanced audio-reactive preset selection. This fork transforms the original random preset system into an intelligent music-aware visualization engine.
- Analyze mathematics, not music - Preset selection based on equation fingerprints, not audio testing
- Autonomous operation - Runs independently once started, requiring minimal backend coordination
- Performance first - Direct WebGL rendering with frame stabilization for consistent 60 FPS
- Community-driven - Built on the extensive Milkdrop preset ecosystem with attribution preservation
- 21,687 Unique Presets - Full collection combines ansorre, Cream of the Crop, projectM, and original Butterchurn packs
- 388 Curated Demo Presets - Alaska Butter collection for lightweight deployments
- Individual Pack Support - Access full-collection packs with 1:1 fingerprint mapping for targeted selection
- Intelligent Selection - Real-time audio analysis drives preset switching using equation-based fingerprints
- Smooth Transitions - Fixed blending system provides seamless crossfades (no more fade-to-black)
- Enhanced Audio Processing - 2048-sample FFT buffer for superior bass response and frequency resolution
- Scene-Based Switching - Moving Average crossover detection for musical scene transitions
- Visual Regression Testing - Deterministic rendering for reliable automated testing
-
Meyda.js Spectral Analysis - Advanced audio features including MFCC, spectral centroid, flatness, rolloff, and sharpness
-
BPM Detection & Beat Tracking - Onset-based BPM detection with 4-beat bars and 16-beat phrase tracking
-
Phrase-Aligned Switching - Preset changes synchronized to musical phrase boundaries (every 16 beats)
-
Pre-Drop Anticipation - Detects energy buildups and switches 1.5 seconds before the drop
-
Mood-Aware Selection - Real-time mood classification with 10 mood types:
- Primary: aggressive, relaxed, happy, electronic, acoustic
- Extended (v2.1): mystical, hypnotic, psychedelic, dreamy, meditative
Mood Triggers (DOC-3):
Mood Audio Triggers Visual Characteristics aggressive High bass (>0.7), loud, fast BPM High energy, sharp edges relaxed Low energy (<0.4), smooth dynamics Soft motion, organic flow happy Major tonality, bright mids Warm colors, upward motion electronic High flatness (>0.5), processed Synthetic, precise patterns acoustic Low flatness, dynamic range Natural, organic forms mystical High rolloff, soft transients Cool colors, ethereal glow hypnotic Steady rhythm, moderate energy Repetitive patterns, iteration psychedelic High flux, wide dynamic range Vivid colors, complex motion dreamy High centroid, low sharpness Soft focus, flowing shapes meditative Very low energy, slow tempo Minimal motion, calm colors -
Genre Detection - Automatic genre classification (EDM, dubstep, hip-hop, rock, classical, ambient, pop) with timing adjustments
-
Performance Degradation Tracking - Automatic preset switching when match quality drops 40%+ from baseline
-
v2.1 Fingerprint Schema - Enhanced fingerprints with expanded mood vocabulary, improved complexity scaling, and keyword-based style detection
-
CLIP Visual Classification - ML-based preset categorization into 8 visual style categories
-
Adaptive FFT Recommendations - Device-aware FFT size optimization suggestions
- 25-30% faster rendering through direct WebGL output (no Canvas 2D intermediate)
- 40% better frequency resolution with 4x larger audio buffers
- Consistent 60 FPS via frame stabilization system
- Memory efficient with proper buffer cleanup during preset transitions
- Modern browser with WebGL2 support (Chrome 58+, Firefox 51+, Safari 15+)
- Web Audio API support for audio analysis
- Minimum 2GB RAM recommended for full preset collection
import isButterchurnSupported from "butterchurn/lib/isSupported.min";
if (isButterchurnSupported()) {
// Initialize Butterchurn
} else {
// Show fallback or upgrade message
}Experience Butterchurn's intelligent music visualization at alaskabutter.com:
- 🎵 Load any audio file or use the built-in demo song
- 🤖 AI-driven preset selection that matches your music's energy
- 📱 Works on any device - desktop, laptop, tablet, or mobile
- 🧪 Advanced test interface at alaskabutter.com/test.html
No installation required - just open your browser and start visualizing!
# Clone repository (Enhanced Fork)
git clone https://github.com/geeks-accelerator/butterchurn.git
cd butterchurn
# Install dependencies (legacy flag required for eel-wasm)
npm install --legacy-peer-deps
# Download preset source files (optional, needed for fingerprint regeneration)
./setup-full-presets.sh
# Build for production
npm run build
# Run development server with watch mode
npm run dev
# Start local test server on port 8192
# (8192 = 2^13, a power of 2 matching audio buffer sizes)
npm run serve:test
# Then open http://localhost:8192/intelligent-selector-test.html<!-- Core Butterchurn library (Enhanced Fork) -->
<script src="https://geeks-accelerator.github.io/butterchurn/cdn/butterchurn.min.js"></script>
<!-- Alaska Butter - Unified preset collection (388 unique presets) -->
<script src="https://geeks-accelerator.github.io/butterchurn/cdn/presets/alaskaButter.min.js"></script>
<!-- Or load individual preset packs -->
<script src="https://geeks-accelerator.github.io/butterchurn/cdn/presets/butterchurnPresets.min.js"></script>
<script src="https://geeks-accelerator.github.io/butterchurn/cdn/presets/butterchurnPresetsExtra.min.js"></script>
<!-- Fingerprint databases for intelligent selection -->
<script type="module">
// Load fingerprint loader
import FingerprintLoader from './src/fingerprintLoader.js';
const loader = new FingerprintLoader();
await loader.loadAllFingerprints('/cdn/presets/');
console.log(`Loaded ${loader.getStats().totalPresets} preset fingerprints`);
</script><!-- Core Butterchurn library -->
<script src="https://unpkg.com/butterchurn@latest/dist/butterchurn.min.js"></script>
<!-- Preset collections -->
<script src="https://unpkg.com/butterchurn-presets@latest/dist/butterchurn-presets.min.js"></script>
<!-- Feature detection -->
<script src="https://unpkg.com/butterchurn@latest/dist/isSupported.min.js"></script># Using npm
npm install butterchurn butterchurn-presets
# Using yarn
yarn add butterchurn butterchurn-presets
# Using pnpm
pnpm add butterchurn butterchurn-presetsimport butterchurn from 'butterchurn';
import butterchurnPresets from 'butterchurn-presets';
// Initialize Web Audio
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const canvas = document.getElementById('canvas');
// Create visualizer
const visualizer = butterchurn.createVisualizer(audioContext, canvas, {
width: 800,
height: 600,
pixelRatio: window.devicePixelRatio || 1
});
// Connect audio source
const audio = document.getElementById('audio');
const source = audioContext.createMediaElementSource(audio);
visualizer.connectAudio(source);
// Load and start preset
const presets = butterchurnPresets.getPresets();
const presetKeys = Object.keys(presets);
visualizer.loadPreset(presets[presetKeys[0]], 0.0);// Load intelligent selector with new fingerprint system
import IntelligentPresetSelector from './src/intelligentPresetSelector.js';
// Create intelligent selector with optional audio context for advanced features
const selector = new IntelligentPresetSelector(visualizer, {
// Configuration options
energyMatch: 0.25, // Energy matching weight
moodMatch: 0.15, // Mood affinity weight
bpmMatch: 0.10, // BPM range matching weight
spectralMatch: 0.10 // Spectral similarity weight
}, audioContext, audioSource); // Optional: for Meyda.js integration
// Initialize with Alaska Butter preset collection
await selector.initialize({
basePath: '/presets/', // Location of preset and fingerprint files
autoLoadPacks: true // Automatically load all preset JS files
});
// Trigger BPM detection when audio is loaded
audioElement.addEventListener('canplay', async () => {
const audioBuffer = await audioContext.decodeAudioData(audioData);
await selector.onAudioLoaded(audioBuffer);
console.log('Detected BPM:', selector.audioAnalyzer.detectedBPM);
});
// Render loop with intelligent selection
function animate() {
const audioLevels = {
timeByteArray: new Uint8Array(visualizer.audio.timeByteArray),
timeByteArrayL: new Uint8Array(visualizer.audio.timeByteArrayL),
timeByteArrayR: new Uint8Array(visualizer.audio.timeByteArrayR)
};
// Intelligent preset selection with phrase-aligned switching
const result = selector.update(audioLevels);
// Access detected features
if (result.features) {
console.log('Current mood:', result.features.mood?.label);
console.log('Beat phase:', result.features.beatInfo?.phrasePosition, '/ 16');
}
// Render with audio data
visualizer.render({ audioLevels });
requestAnimationFrame(animate);
}
animate();// Access real-time audio analysis
const analyzer = selector.audioAnalyzer;
// Get current beat information
const beatInfo = analyzer.trackBeatPhase();
if (beatInfo) {
console.log(`BPM: ${beatInfo.bpm}`);
console.log(`Beat: ${beatInfo.beatPosition + 1}/4`);
console.log(`Bar: ${beatInfo.barPosition + 1}/4`);
console.log(`Phrase: ${beatInfo.phrasePosition + 1}/16`);
console.log(`Is phrase boundary: ${beatInfo.isPhraseBoundary}`);
}
// Get mood detection
const features = analyzer.calculateFeatures(freqData, timeData);
const mood = analyzer.detectMood(features);
console.log(`Mood: ${mood.label} (${(mood.confidence * 100).toFixed(0)}% confidence)`);
// Detect buildups (for pre-drop anticipation)
const buildup = analyzer.detectBuildup(features);
if (buildup.isBuildup) {
console.log(`Buildup detected! Drop ETA: ${buildup.dropETA}ms`);
}
// Manual preset selection with mood/BPM matching
const mood = { label: 'electronic', confidence: 0.8 };
const bestPreset = selector.selectBestPreset(features, mood);// Load specific preset using 8-character hash ID
const presetHash = 'a3f7b2c9'; // Content-based hash
await selector.loadPresetByHash(presetHash, 2.0); // 2-second crossfade
// Get preset info by hash
const preset = await selector.getPresetByHash(presetHash);
console.log('Loaded:', preset.name, 'by', preset.author);const visualizer = butterchurn.createVisualizer(audioContext, canvas, {
width: 1920,
height: 1080,
pixelRatio: 2, // High DPI support
textureRatio: 1, // Texture resolution multiplier
meshWidth: 48, // Warp mesh resolution
meshHeight: 36,
targetFPS: 60, // Frame stabilization target
outputFXAA: true, // Anti-aliasing
deterministic: false, // Enable for testing
testMode: false // Deterministic with seeded RNG
});// Load specific preset with blend time
const presetName = 'Flexi - mindblob mix';
const preset = presets[presetName];
visualizer.loadPreset(preset, 2.0); // 2-second crossfade
// Resize visualizer
visualizer.setRendererSize(1600, 1200);
// Connect different audio sources
const micSource = await navigator.mediaDevices.getUserMedia({ audio: true });
const micSourceNode = audioContext.createMediaStreamSource(micSource);
visualizer.connectAudio(micSourceNode);Audio Input → Web Audio API → 2048-sample FFT → Audio Analysis
↓
Preset Database ← Intelligent Selector ← Audio Features
↓
WebGL2 Renderer ← Butterchurn Core ← Selected Preset
↓
Canvas Output (60 FPS) ← Frame Stabilizer
- Render Time: 8-10ms average (target <10ms for 60 FPS)
- Memory Usage: ~200MB with full preset collection
- Audio Latency: ~20ms from audio to visual response
- Preset Switch Time: 2-5 seconds with smooth crossfades
- CPU Usage: 15-25% on modern hardware
- GPU Usage: Moderate (optimized shaders, minimal state changes)
- Audio Sources: MediaElement, MediaStream, AudioBuffer, external AudioNode
- Preset Formats: Original .milk files, JavaScript preset objects
- Output: Direct WebGL2 rendering to canvas
- Browsers: Chrome 58+, Firefox 51+, Safari 15+, Edge 79+
Each preset is analyzed by its mathematical equations to generate:
v1.0 Fields (Core):
- Content Hash: 8-character SHA256-based unique identifier for deduplication
- Energy Score: Complexity based on equation analysis and variable usage
- Bass Reactivity: Frequency of bass-related variables (bass, bass_att, etc.)
- Treble Reactivity: Usage of treble frequency variables (treb, high, etc.)
- Performance Estimate: Shader complexity scoring for FPS estimation
- Pack Attribution: Source pack tracking with author and name preservation
v2.0 Fields (Intelligent Selection):
- Mood Affinities: Compatibility scores for aggressive, relaxed, happy, electronic, acoustic
- Optimal BPM Range: Min/max/ideal BPM for best visual synchronization
- Color Profile: Dominant color temperature (warm, cool, nature, neutral)
- Motion Speed: Visual motion category (slow, medium, fast)
- Visual Style: Primary visual category from ML classification
- Visual Style Scores: CLIP confidence scores across 8 visual categories
- butterchurnPresetsAll: 21,687 unique presets (deduplicated mega-collection)
- Alaska Butter: 388 unique presets (curated demo collection from 6 original packs)
- Individual Packs: Original packs with 1:1 fingerprint mapping for targeted selection
| Source | Presets | Description |
|---|---|---|
| ansorre collection | 12,108 | Community-curated presets with CLIP visual classification |
| Cream of the Crop (COTC) | 6,876 | Best-of selections from the Milkdrop community |
| projectM Classic | 2,315 | Classic projectM presets ported to Butterchurn |
| Original Butterchurn | 388 | Deduplicated presets from 6 original preset packs |
- Verify
audioLevelsparameter passed torender()method - Check Web Audio API permissions (microphone/media access)
- Ensure audio source is properly connected to visualizer
- Test with different audio sources to isolate issue
- Check WebGL2 support:
isButterchurnSupported() - Verify canvas element exists and has proper dimensions
- Check browser console for WebGL context errors
- Test with minimal preset first before complex ones
- Reduce canvas resolution or
pixelRatio - Disable
outputFXAAanti-aliasing - Close other GPU-intensive browser tabs
- Check performance with
test/performance-test.html
- Verify fingerprint database loaded correctly
- Check preset pack contains valid preset objects
- Test intelligent selector pause/resume functionality
- Validate preset completeness before loading
- Use
npm install --legacy-peer-depsfor eel-wasm compatibility - Clear
node_modulesand reinstall if WASM errors occur - Check Node.js version compatibility (14+ recommended)
- Verify TypeScript and AssemblyScript toolchain versions
- Webamp - Winamp 2.9 reimplementation
- Butterchurn Extension - Browser extension for any audio
- Nightride FM - Live DJ streaming with visualizations
- mStream - Personal music streaming server
- Fork repository and create feature branch
- Install dependencies:
npm install --legacy-peer-deps - Make changes following code style (ESLint + Prettier)
- Run tests:
npm run analyze && npm run test:visual - Test performance:
npm run build && open test/performance-test.html - Submit pull request with clear description
This project enforces code quality through multiple linters and validators:
# Run all code quality checks
npm run analyze
# Individual linters
npm run lint:check # ESLint - JavaScript code style
npm run typecheck # TypeScript - Type checking (no emit)
npm run lint:glsl # GLSL - Shader code validation
# Auto-fix linting issues
npm run lint # ESLint with --fix flag
# Pre-commit check (runs all analyzers)
npm run precommit-
ESLint: JavaScript/TypeScript code style and best practices
- Parser:
@typescript-eslint/parser - Plugins:
import,jsdoc,prettier - Extends:
eslint-config-prettierfor Prettier integration
- Parser:
-
TypeScript: Static type checking with
tsconfig.json- Strict mode enabled
- No implicit any
- ES2020 target with ES modules
-
GLSL Linter: Custom shader validation (
tools/glsl-lint.js)- Validates WebGL shader syntax
- Checks for common GLSL errors
- Ensures shader compatibility
-
Prettier: Code formatting
- Integrated with ESLint
- Consistent code style across the project
- Auto-formats on lint --fix
Critical for preventing rendering bugs:
npm run test:visual # Run visual tests
npm run test:visual:update # Update snapshots (verify first!)
npm run test:visual:view # View test differencesPlease include:
- Browser version and operating system
- Steps to reproduce
- Expected vs actual behavior
- Console errors or warnings
- Minimal example if possible
This project is licensed under the MIT License - see the LICENSE file for details.
- Ryan Geiss for creating the original MilkDrop
- Nullsoft for Winamp and the visualization ecosystem
- Jordan Berg for the original Butterchurn WebGL implementation
- Performance optimization insights from production streaming applications
- ansorre for curating the 12,108-preset collection with CLIP visual classifications
- Cream of the Crop (COTC) community for the 6,876-preset best-of collection
- projectM team for the 2,315 classic presets ported to the Butterchurn format
- Preset artists including Flexi, Geiss, Martin, Rovastar, Aderrasi, Unchained, Cope, Shifter, and hundreds of community contributors who created the original Milkdrop presets
