🎵 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
- 388+ Unique Presets - Alaska Butter collection combines 6 preset libraries with mathematical deduplication
- 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
- 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 (auto-loads fingerprints)
const selector = new IntelligentPresetSelector(visualizer);
// 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
});
// 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 Moving Average crossovers
selector.update(audioLevels);
// Render with audio data
visualizer.render({ audioLevels });
requestAnimationFrame(animate);
}
animate();// 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:
- 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
- Alaska Butter: 388 unique presets (deduplicated from all 6 packs)
- Full Collection: Individual packs with 1:1 fingerprint mapping
- Total Available: 553 presets before deduplication (160 duplicates removed)
- 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
- Preset creators including Flexi, Geiss, Martin, Rovastar, and hundreds of community contributors
- Performance optimization insights from production streaming applications
