One file.
Full screen.
On beat.
Built for a Carnival DJ set with no budget, no time to learn a VJ tool, and a laptop already busy running audio. A single HTML file that opens in a browser tab and reacts to whatever's playing in the room.
Four patterns, running right now.
Oscilloscope & Audio Spark
Frequency-based waveform distortion with triggered particle sparks.
Quantum Nodes & Liquid Blob
Interconnected audio-reactive nodes and beat-synced fluid dynamics.
Flowing Waves & Particle Storm
Phase-shifting sine waves rendered within a chaotic particle field.
Fractal Sync
Recursive geometric structures scaling to bass frequencies.
Simplified, auto-animated recreations for this page โ the real thing reacts to a live microphone.
They are not interchangeable.
The repo ships two standalone versions with real functional differences โ read this before picking one for a live set.
Base
pattern-drawer.html
- 14 patterns
- Random rotation order
- Timed rotation works correctly, respects
minutesPerPattern - On-screen debug status โ mic state and pattern name
- No logo overlay, no manual BPM, no ultrawide fillers
Extended
pattern-drawer_audio_sync_&_image.html
- 22 patterns
- Sequential rotation order
- Logo overlay, cycles local images with a beat-reactive pulse
- Manual BPM fallback with +/- keys
- Ultrawide filler layers for wide projection
minutesPerPattern is defined but never read. Fix: swap the hardcoded 5000 for this.switchInterval.
Recommended for live events: the extended version โ just go in knowing about the timing bug. Want something lighter with correctly-working timed rotation instead? Start from the base file.
Three calls made under a deadline.
Vanilla JS, not a VJ framework or Three.js
The target was a browser tab on a laptop already under CPU load from audio software. Every dependency โ bundler, WebGL context, external library โ is a potential failure point during a live set. Canvas 2D with no build step opens in under a second and has nothing that can fail to load.
Two files, not one versioned one
The base file was the original, used at the actual event. The extended version came later with new patterns and the logo overlay. Keeping them separate means the working original is always there as a fallback โ a live-event safety net worth more than a clean repo.
getUserMedia, not createMediaElementSource
It's built to react to whatever's playing through the room โ DJ software, Spotify, a stream โ without routing that audio through the browser. A microphone captures the room's sound regardless of source, which is the only approach that works when the audio chain lives outside the browser entirely.
Click to start. Then it's hands-off.
file://. python3 -m http.server 8000 is enough.Click anywhereInitialize the audio context and request microphone accessDToggle debug mode โ cycles through every pattern for previewNSkip to the next pattern immediately+ / -Adjust manual BPM โ extended version only, when the mic is inactiveGet it running before the set starts.
Clone the repository
git clone https://github.com/mirconegri/CarnivalVisualEffects.git
Serve it locally
python3 -m http.server 8000 โ required for microphone access.
Open it
localhost:8000/pattern-drawer.html, or the extended version.
Click anywhere
Grants microphone access, and the patterns start reacting.
Built in the open, tested on a dance floor.
Look at the code, fix the rotation bug, or add your own pattern.