Turn whatever your computer is playing — Spotify, YouTube, a DAW, a DJ set, a game — into audio-reactive visuals on one display or ten. A real MilkDrop engine, 59 visualizer modes, layers and 40 GPU effects, an OBS overlay, Spout / Syphon, projection mapping, RGB lighting, a timeline and clip deck for live shows, and AI control over MCP.
Windows · macOS · Linux — no account, no telemetry, MIT licensed.
Run from source · Features · FAQ · Türkçe
| 59 visualizer modes | 43 backgrounds | 40 GPU effects | 18 scene transitions |
|---|---|---|---|
| 72 ready-made scenes | 98 formulas + 13 3D solids | 42 built-in GLSL shaders | 58 colour palettes |
| 10,347 MilkDrop presets tested | 96 MCP tools for AI agents | 17 layer blend modes | 2 languages (EN / TR) |
About the version labels. The current release is v3.1.4. Everything on this page is in the source on
main; items marked 3.1.5 ship with the next release, v3.1.5, and are not in the v3.1.4 download yet. Run from source to use them today.
- No Stereo Mix, no virtual cable on Windows. System audio is captured straight from your speakers or headphones (WASAPI loopback), alongside microphones and line inputs — or from one application only, so the visuals follow Spotify and ignore the game and the voice chat. → Audio
- Every screen, one engine. Full-screen windows on every display you pick, a transparent OBS browser source, Spout / Syphon for Resolume, TouchDesigner and MadMapper, and a phone remote — all drawing the same picture. → Outputs
- MilkDrop that really runs your presets. Per-frame and per-pixel equations, the warp mesh, HLSL shaders translated to GLSL, measured against 10,347 real presets and frame by frame against a reference MilkDrop 2 renderer. Import whole libraries, browse them as thumbnails, edit presets live, or generate new ones 3.1.5. → MilkDrop
- A compositor, not a single effect. Unlimited layers, 17 blend modes, masks, an A/B crossfader, 40 GPU effects per layer or on the whole frame, and a modulation matrix that routes LFOs, envelopes and live audio analysis to any setting. → Layers
- Built for live shows. A timeline with automation lanes, a clip deck with beat-quantised launching, Auto VJ that changes scenes on the bar, tap tempo, MIDI and OSC. → Show control
- Ready for the stage. Projection mapping with corner pin, mesh warp and soft-edge blending for multi-projector rigs, plus aspect correction for LED walls whose pixels are not square. → Stage
- Your lights follow the music. Windows Dynamic Lighting, OpenRGB on every platform and Art-Net / DMX, all fed by one renderer — they can even take their colours from the MilkDrop picture 3.1.5. → Lighting
- For creators. Frame-exact offline video export from an audio file, one-key recording, music-video layouts, a Now Playing overlay with album art, and synced lyrics (LRC / SRT). → Export
- AI-ready. A local MCP server with 96 tools lets Claude, Codex, Cursor and other agents
read the show, build scenes and run the outputs — behind five permission levels, on
127.0.0.1only 3.1.5. → MCP - Private and offline. No account, no telemetry, no cloud. The only call the application makes on its own is GitHub's latest-release check, and you can switch it off. → Privacy
The spectrum bars, waves and rings the application started with — still one click away, on fluid gradient backgrounds that move with the music.
Every scene image and clip on this page is rendered by the application itself with
npm start -- --shots, driven by a synthetic 120 BPM signal. With real audio the visuals follow the music instead.
Get the latest build from Releases.
| Platform | File | Notes |
|---|---|---|
| Windows 10 / 11 | …-windows-setup.exe |
Recommended. Registers the Dynamic Lighting identity so lights keep running in the background, and can update itself. |
| Windows, portable | …-windows-portable.exe |
No installation. Dynamic Lighting works only while the app is focused. |
| macOS (Apple Silicon) | …-macos-arm64.dmg / .zip |
Unsigned — see macOS first launch. System audio needs a virtual device such as BlackHole. |
| Linux (x64) | …-linux-x86_64.AppImage / …-linux-amd64.deb |
Needs PulseAudio or PipeWire. The AppImage can update itself 3.1.5. |
Nothing else needs to be installed: the audio helper runs on the application's own binary.
- Pick one or several displays from the Displays menu at the top, then one or more audio sources under Audio.
- Click Open Visualizer. The visual opens full-screen on every selected display.
- Change anything on the right — it applies immediately and saves itself.
- Or start from a finished look: Library → Templates has 72 of them.
- For streaming, turn on Output → Streaming Output and paste the address into an OBS Browser Source.
- Press ESC on any visualization window to close them all.
You need Node.js 20 or newer (CI tests 20 and 22) and Git.
git clone https://github.com/CaYatur/SoundVisualizer.git
cd SoundVisualizer
npm install
npm startnpm install rebuilds the native audify audio module for Electron automatically (postinstall).
If the audio helper reports INVALID_HELPER_OUTPUT, run npm run rebuild:audio, then
npm run check:runtime. Behind a corporate proxy that breaks certificates, set
NODE_OPTIONS=--use-system-ca before npm install. Developer mode with DevTools: npm run dev.
| You are… | What you get |
|---|---|
| A streamer | A transparent overlay for OBS as a browser source, a Now Playing card with album art, lyrics, and a phone remote to change scenes mid-stream. Runs on one PC while OBS runs on another. |
| A VJ or live performer | A timeline, a clip deck with beat-quantised launching, Auto VJ on the bar, MIDI and OSC control, blackout, Spout/Syphon into Resolume or TouchDesigner, and full-screen output on every projector. |
| A music producer or label | Frame-exact video export from your track, eight restrained music-video layouts with your logo and the track title, and a recorder for the live output. |
| A venue, event or installation | Projection mapping, edge blending, aspect correction for LED walls, accidental-close protection, crash recovery, and a display that never goes to sleep mid-show. |
| An RGB or ambient lighting fan | Windows Dynamic Lighting, OpenRGB and Art-Net/DMX driven by the music, with a screen-saver-style template group for background visuals. |
| A MilkDrop fan | A MilkDrop engine that loads .milk and .milk2, imports whole libraries, shows thumbnails, keeps favourites, ratings and tags, and lets you edit or generate presets. |
| A developer or AI tinkerer | A GLSL editor with Shadertoy and ISF import, an MCP server with 96 tools, a documented config, and 2,700+ tests that check answers rather than lines. |
The sections above are the short version. Everything below is the full list, by category.
- Audio capture and analysis
- Visualizer modes and backgrounds
- MilkDrop
- Layers, masks and effects
- Modulation
- 3D geometry and formulas
- Studio — write your own shader
- Scenes, templates, transitions and colour
- Text, lyrics and Now Playing
- Logo, images and the media layer
- Show control: timeline, clip deck, Auto VJ, MIDI and OSC
- Outputs: screens, OBS, Spout and Syphon
- Stage: projection mapping and aspect correction
- RGB lighting: Dynamic Lighting, OpenRGB and Art-Net
- Recording and video export
- MCP — control from an AI agent
- The control panel
- Reliability, power and updates
- Privacy and security
Sources
- System output — speaker or headphone loopback. No "Stereo Mix" needed.
- Microphones and line inputs, captured the same way.
- Single applications (Windows) — WASAPI process loopback. Capture only the chosen apps, or everything except one. Targets are stored by executable name, so a restarted app reattaches by itself, and an app that is not running yet is picked up when it starts. Needs Windows build 20348 or newer. macOS and Linux report why it is not available there yet.
- Several sources at once, mixed before analysis — "Spotify + microphone" is one selection.
- Both channels reach the visuals 3.1.5. Each frame carries the left and right channels next to the mono mix, so stereo width, correlation and the Goniometer measure the real stereo image. Until v3.1.5 the capture helper averaged the two channels away: width sat at 0, correlation at 1, and the Goniometer drew a vertical line for every song.
- Recovers by itself 3.1.5. If the capture helper exits or its frames stop (sleep and resume, a device unplugged), capture is rebuilt with a growing back-off, and the panel says "reconnecting" instead of pretending to capture. A device that drops in and out is not retried in a tight loop.
- Sensitivity, smoothing and bass emphasis, a mains-hum guard for 50/60 Hz, and live meters for overall, bass, mid and treble.
How capture works. Capture runs in the main process, not in the browser window. A helper reads the device — WASAPI loopback on Windows, CoreAudio on macOS, PulseAudio or PipeWire on Linux — computes the FFT, and sends frames to every renderer.
- On macOS, system audio needs a virtual device such as BlackHole; microphones work directly. macOS has no loopback of its own, so there is no way around this.
- On Linux, system audio is the PulseAudio or PipeWire monitor of your output device. It is an input device; the application marks it as loopback and prefers it by default.
Spectrum metering. Bars are measured, not guessed.
- Frequency scale — logarithmic, linear, mel or bark.
- Amplitude scale — linear or decibel, with a settable floor from −24 to −96 dB. dB is what makes quiet detail visible instead of flattened against the baseline.
- Ballistics — separate attack and release, so bars snap up and fall smoothly. Frame-rate independent.
- Neighbour spread (widens peaks without flattening them) and profile smoothing (a symmetric neighbour average).
- Spectral tilt in dB per octave, neutral at 1 kHz, so the top end is not permanently dead.
- Bands narrower than one FFT bin are interpolated at the band's centre frequency, so neighbouring bars read their own values instead of sharing one bin. 27 tests cover the engine, including one that asserts the bar profile has no step in it.
Deep analysis. Every measurement has a live meter and is available as a modulation source.
- Musical key and chord from a constant-Q chroma vector — a Goertzel filterbank rather than FFT bins, because at 2048 samples the bass register is not resolvable by bins — with Krumhansl-Schmuckler key profiles and chord templates.
- Pitch tracking with YIN.
- Harmonic / percussive separation, and per-band onset detectors for kick, snare and hat.
- Spectral descriptors — centroid, rolloff, flatness, crest.
- Loudness, dynamics, true peak, stereo width, correlation and mid/side bands.
- Silence detection, auto-gain, and a rolling spectral history buffer.
Tempo. BPM is estimated from a period histogram (tested to ±0.5 BPM against 90, 120, 128, 140 and 174 BPM signals), with tap tempo and a BPM lock shared by Auto VJ, the clip deck and MilkDrop.
These two sheets are generated from the mode catalogue itself, so a new mode appears in them the next time the screenshots are rendered. Any visualizer can sit on any background, and with layers you can stack as many of each as you like. Modes added since v3.1.4 are part of 3.1.5.
Visualizer modes — 59
- Basic — Bars · Center · Blocks (LED equaliser) · Dot Matrix · City Skyline (buildings with lit windows)
- Waveform — Wave (oscilloscope) · Ribbon (waveform history) · 3D Wave (history stacked in perspective) · Lissajous (XY oscilloscope) · Strings (each string vibrates with its band) · Terrain (perspective wireframe landscape) · Ridgelines · DJ Waveform
- Radial — Circle · Radial Wave · Starburst · Arcs (one arc per band) · Pinwheel · Mandala (polar rose curve) · Kaleidoscope · Vortex · Helix · Tunnel · Orb · Radar Chart
- Particles and events — Particle · Fireworks (bursts on the beat) · Lightning (branching bolts on bass) · Bubbles · Liquid Drop (metaballs) · Ripple Grid (rings spreading on the beat) · Spectrogram · Confetti · Beat Pads · Bouncing Balls
- Generative systems — Flow Field (particles steered by a noise field) · Flock (boids driven by the spectrum) · Voronoi · Truchet · Moiré · Wave Interference · Ropes (verlet physics kicked by onsets) · Galaxy · DNA Helix · Isometric City · Attractor Field (discrete maps from the formula library with two parameters bound to audio) · Pendulum Wave · Cardioid
- Text — Text / Lyrics · Now Playing
- Metering — Oscilloscope (XY) · Goniometer (stereo phase scope) · Chroma Wheel (pitch classes in circle-of-fifths order, highlighting the detected chord root) · VU Meter · Level Meter (PPM)
- Advanced engines — 3D Geometry · MilkDrop · Feedback (the classic infinite-tunnel feedback look) · Studio (your own GLSL shader)
Bar count, minimum and maximum frequency, gap, placement (bottom, centre or full), mirror, line width, amplitude, sensitivity and glow appear whenever they mean something for the selected mode. Colour comes from the scene palette, a custom colour pair, or rainbow.
Backgrounds — 43
- Fluid — Fluid Gradient (an audio-reactive mesh gradient with flow, wander, orbit, swirl, warp, grain and audio hue shift) · Ink (liquid blobs that swirl as they flow) · Nebula (soft gas clouds) · Wave Layers (crests that swell with the audio) · Northern Lights (undulating curtains) · Lava Lamp · Underwater
- Geometric — Retro Grid (a perspective grid to the horizon) · Honeycomb Grid (cells lit by a wave from the centre and by the spectrum) · Mosaic (a cell per frequency band) · Corridor (rings or polygons coming toward you) · Spiral · Pulse Rings (extra rings on bass hits) · Network (drifting linked nodes) · Low Poly · Halftone · Isometric Cubes
- Atmosphere — Starfield · Snow / Embers · Light Particles (bokeh) · Digital Rain · City (a parallax skyline whose windows light up with the music) · Clouds · Stage Lights · Fireflies · Storm
- Generative grounds — Liquid Metal · Plasma · Caustics · Ribbons · Contours · Wave Field · Embers · Sand · Stained Glass · Circuit Board · Prism · Globe Mesh · Wire Tunnel · Hex Pulse · Mirror Pattern
- Other — Solid Colour · Studio (a GLSL shader you wrote yourself)
Transparent background. Turn on Background → Transparent Background and the desktop shows through the visualizer window: a solid colour is not painted, and a background effect's dark parts turn transparent below a Transparency Threshold. Transparency survives the effect chain, so bloom and blur glow over empty pixels without turning the window into a black rectangle.
A MilkDrop engine written for this application — it does not embed projectM or MilkDrop's code.
The preset language runs for real: a tokeniser, a parser and compilation to JavaScript closures;
per_frame and per_pixel equations drive a warp mesh with feedback; the HLSL warp and composite
shaders are translated to GLSL and run on the GPU.
- Measured, not claimed. Over a 10,347-preset corpus from the projectM original and
cream-of-the-crop packs, every preset loads and runs, all 16,346 shader stages compile, and about
98% produce a live image. Fidelity is checked frame by frame against a reference renderer built
from BeatDrop's MilkDrop 2 sources. Both harnesses are in
scripts/, so the numbers can be reproduced. - A MilkDrop Fidelity switch follows MilkDrop 2's own rules — its compiler, its audio chain, its mesh transform, its default values and its fixed-function pipeline. The details are in MilkDrop engine notes at the end of this page.
- Five presets of our own ship with the app — Aurora, Molten Gold, Still Rings, Endless Tunnel and Pulse Weave — so the engine shows what it does before you import anything. No third-party preset pack is bundled.
- Import a whole library 3.1.5 — from a ZIP pack, a folder, or by searching this computer (Winamp, foobar2000 and projectM folders, Downloads, Desktop, Music, Documents). You see what will be added before anything is copied; duplicates are skipped; textures come along; folder names can become tags. A 9,795-preset folder imports in about six seconds.
.milkand.milk23.1.5 — MilkDrop 3's double presets load as a frozen blend of their two presets, and a Preset Format setting reads files with MilkDrop 2 or MilkDrop 3 rules (16 custom waves and shapes,q1–q64), or picks automatically.- Thumbnails 3.1.5 — a Grid layout shows each preset as a small picture, drawn once in the background and redrawn only when the preset or a texture it uses changes.
- Favourites, tags, ratings and search by author 3.1.5 —
author:geissfinds Geiss's presets,#calmfinds a tag; random order is weighted by rating as MilkDrop weights it; Previous and Next walk a 64-step history of what was actually shown. - A live editor 3.1.5 — frame and pixel equations, waves, shapes, warp and composite shaders each have a tab; a change shows in the running picture a moment after you stop typing; errors point at the preset's own line. The original is never changed.
- A preset generator and mash-ups 3.1.5 — write an original preset from four sliders (energy, warmth, density, motion) and a seed that brings the same preset back, or build one from parts of the presets you already have. Generated presets never flash.
- Show control 3.1.5 — auto advance by seconds or on the bar, hard cuts on loud moments, next preset on track change, a lock, and MilkDrop actions on MIDI and OSC. Every screen shows the same preset with the same random seed.
- User textures and sprites 3.1.5 — point at a MilkDrop
texturesfolder, and launchmilk_img.inisprites with MilkDrop's own keys or from a controller. - Safe to watch 3.1.5 — flash limiting at WCAG 2.3.1's general-flash value, held per second so a 144 Hz screen is as safe as a 30 fps one, and the system's reduce motion setting is honoured.
- Survives a GPU reset 3.1.5 — a lost WebGL context is rebuilt on the same canvas, and the running preset carries on with the same equation state and clock.
The Feedback engine is MilkDrop's family resemblance without presets: zoom, rotate, warp and decay sliders, four wave styles, and bass-driven zoom and rotation for the classic infinite tunnel.
- Unlimited layers, each with its own source, blend mode, opacity, transform (scale, rotate, X/Y, flip) and audio response (band, opacity, scale, rotate). The layer at the top of the list is the topmost one in the output.
- 17 blend modes — Normal, Add, Screen, Multiply, Overlay, Darken, Lighten, Colour Dodge, Colour Burn, Hard Light, Soft Light, Difference, Exclusion, Hue, Saturation, Colour, Luminosity.
- Masks — alpha from another layer, plus rectangle, ellipse, linear and radial gradients, with position, size, angle, feather and invert.
- Groups with a single fader, and an A/B crossfader on an equal-power curve.
- Solo, mute and lock — solo isolates a layer reversibly, mute hides one without losing its settings, lock prevents accidental edits.
- Copy, paste and duplicate layers across scenes. The whole stack can be switched off, returning to the plain Background plus Visualizer setup without losing the list.
- One broken layer no longer takes the frame with it 3.1.5, and layers hidden under an opaque MilkDrop layer are not drawn at all.
Post-processing — 40 GPU effects, orderable, each with its own opacity, toggleable, and available per layer as well as on the composite. An effect on a single layer keeps that layer's transparency, so the layers below stay visible.
- Composition — Bloom · Vignette · Trails / Echo · Edge Highlight · Colour Grade
- Blur and focus — Gaussian Blur · Radial Blur · Directional Blur · Zoom Blur · Tilt-Shift · Depth of Field (Bokeh) · Sharpen · Emboss
- Halftone and pattern — Dither (Bayer) · Halftone · ASCII Mosaic · Cross-Hatch (Pen) · Oil Paint (Kuwahara) · Pixelate · Posterize / Invert · Threshold · Solarize
- Analogue and damage — Film Grain · CRT / Scanlines · VHS / Analogue Tape · Glitch (Slice Shift) · Datamosh (Block Shift) · Bad Signal · Chromatic Aberration
- Distortion — Lens Distortion · Twirl · Polar Transform · Ripple Distortion · Slit-Scan · Kaleidoscope · Mirror
- Colour and light — Gradient Map · Levels & Curve · God Rays · Star Filter
Any parameter of any effect can be driven by the modulation matrix.
- Any source to any setting — routes go to any dotted configuration path, chosen from a live tree of the current settings.
- Sources — bass, mid, treble, level, onset envelope and onset trigger · eight spectrum bands · LFOs · envelope followers · sample-and-hold · random · the beat clock · macro knobs · and every deep-analysis measurement (key, chord, pitch, loudness, drum bands…).
- Eight LFO shapes — sine, triangle, saw up, saw down, square, pulse, random ramp and noise — with rate in Hz or in beat divisions (1/16 up to 8 bars) locked to the detected tempo, plus phase offset and pulse width.
- Per-route shaping — minimum, maximum, amount, set or add, a curve (linear, exponent, S-curve, quantise, invert), smoothing and slew limiting.
- Eight macro knobs, exposed to MIDI learn.
- Values are applied copy-on-write, so modulation never alters your saved settings, and LFO phase comes from the draw clock, so offline export is frame-exact.
- Plane curves (30) — rose curves, lemniscates, cardioids, epicycloids, hypocycloids, spirals, roulettes, Lissajous figures, butterfly and superformula curves among them.
- Space curves (12) — trefoil and torus knots, Viviani's curve, helices, conical spirals and similar.
- Surfaces (29) — torus, Klein bottle, Möbius strip, Boy's surface, Dini's surface, breather, superellipsoid, Gielis supershapes, Chladni figures, trefoil tube and more.
- Strange attractors (27) — Lorenz, Rössler, Chen, Halvorsen, Thomas, Aizawa, Chua, Dadras, Sprott, Clifford, de Jong, Hénon and others, continuous and discrete.
- Solids (13) — tetrahedron, cube, octahedron, dodecahedron, icosahedron, a geodesic sphere with subdivision control, four L-systems (tree, fern, dragon curve, 3D Hilbert curve) and three iterated function systems (Barnsley fern, Sierpinski tetrahedron, spiral).
- Render as wireframe, points or shaded, with resolution, deformation, spin, colour mode and audio binding on every parameter.
- Own matrix maths — no third-party 3D library. Framing is measured, not declared: the first iterations of an attractor are probed for their bounding box, and a test asserts every system lands inside the view volume.
- A GLSL editor with live preview, error line reporting and sliders you declare yourself.
- Shadertoy and ISF import through local converters. No service is contacted.
- Shaders receive
sv_resolution,sv_time,sv_level,sv_bass,sv_mid,sv_treble,sv_beat,sv_spec(x),sv_waveAt(x),sv_col(x)for the scene palette, andsv_mediafor the camera or video layer. - 42 built-in shaders, all compiled on a real GPU by the self-test:
- Backgrounds (25) — Cloud Layers · Curl Flow · Lava Lamp · Ink Bleed · Smoke Rings · Hex Flow · Warped Grid · Truchet Weave · Moiré Interference · Crystal Cave · Mandelbrot Zoom · Julia Set · Burning Ship · Apollonian Gasket · Kaleidoscopic IFS · Menger Sponge · Mandelbulb · Light Tunnel · Star Warp · Aurora Curtain · Liquid Metal · Neon Rain · Reaction Pattern · Water Caustics · Prism Glow
- Visualizers (11) — Glowing Bars · Spectrum Ring · Wave Field · Beat Burst · Glowing Oscilloscope · Frequency Mesh · Note Ring · Particle Flow · Kaleidoscope Spectrum · Pulse Grid · Liquid Bars
- Six earlier presets — Plasma Sea, Frequency Rings, Liquid Metal, Star Gate, Wave Curtain, Bass Sphere
- MilkDrop Preset Generator and Editor live here too — see MilkDrop.
Scenes store the whole look — background, visualizer, layers, effects, logo, text, modulation, MilkDrop and visual objects — under a name. Restore with one click, update from the current look, export and import as JSON.
72 templates in nine groups. One click, and your audio device, display selection, streaming and lighting settings are left alone — trying a template must not damage a working setup, and a test asserts it.
- Club (8) — Strobe Wall, Hyper Tunnel, Laser Grid, Mandala Drop, Strobe Floor, Fireworks, MilkDrop Flow, Strange Attractor
- Ambient (9) — Aurora, Ink in Water, Topography, Underwater, Embers, Liquid Metal, Night Globe, Flow Field, Interference
- Streaming (6) — Corner Bars, Clean Wave, Ring Meter, Scope Overlay, Lower Third, Studio Meters
- Music Video (8) — Label Card, Artwork Card, Baseline Bars, Amber Room, Minimal White, Quiet Frame, Corner Meter, Centre Strip
- Music (6) — Chroma Wheel, Helix, Silk Ribbons, Strings, Spectrogram, Galaxy
- Screensaver (6) — Plasma, Stained Glass, Circuit, Wire Tunnel, Dunes, Prism
- 3D Geometry (8) — Klein Bottle, Lorenz, Supershape, Trefoil Tube, Chladni, Rose Curve, Chua Circuit, Möbius
- Genre (16) — Techno, House, Drum & Bass, Hip-Hop, Lo-Fi, Synthwave, Rock, Metal, Jazz, Classical, Ambient, Pop, Trance, Dubstep, Chiptune, Experimental
- Event (5) — Minimal Line, Corporate, Gala, Festival, Projection Test
Music video layouts. Restrained layouts for release videos and official channels, deliberately separate from the club material: bar placement as a fraction of the frame, your logo beside the bars rather than behind them, and the track title and artist as separate layers.
Scene Generator. Builds a scene from a description. It is not a neural network and is not presented as one: it reduces the text to four axes with a weighted keyword dictionary and seeds a deterministic generator from them. It runs entirely offline.
18 scene transitions — Cut · Crossfade · Dissolve · Wipe · Radial · Clock · Barn · Blinds · Stripes · Checker · Iris · Luma (keyed on the outgoing frame's own luminance) · Zoom · Push · Slide · Flash · Glitch · Blur. Six easing curves, a duration in seconds or in beats, and a switch to turn them off. They fire on a change of scene, never on a slider, so dragging a control never starts one.
Colour. Five colour stops, 58 built-in palettes in seven groups (Classics, Warm, Cool, Neon & Cyber, Dark, Light, Monochrome Families) and your own saved palettes apply to every background, to Studio and to the 3D engine. Every visualizer can follow the palette (theme colour mode).
Dynamic colour theme (Windows). The palette can follow the playing track: colours extracted from the album cover, a harmony style (analogous, complementary, triadic, cyberpunk, synthwave, aurora…), the mood of the title, or a cycle through presets — applied to the background, the visualizer, or both.
Now Playing. On Windows the application reads the system media session (SMTC), so the track from Spotify, YouTube Music, a browser or most players appears on screen by itself: title, artist, album, elapsed and remaining time, a progress bar and the album cover. It can stay on screen or appear only when the track changes, with seven animations and Modern and OG styles. On macOS and Linux the title, artist and cover are entered by hand. Now Playing also shows in the panel preview and in exported videos 3.1.5.
Text. Audio-reactive typography with per-character response (scale, jitter, lift) · font, weight, size, alignment, position, opacity, outline and shadow · animation presets · marquee and ticker · karaoke highlighting.
Lyrics.
- LRC and SRT import, the format detected from the content, enhanced LRC word timings supported.
- A timing editor with a sync offset that writes back to LRC.
- Play, pause and stop for a loaded lyrics file while the screens are open; all screens and the OBS overlay share one clock, corrected to the machine running the app.
- Lyrics library (Windows) 3.1.5 — keep many LRC or SRT files, each with an artist and a title. Follow the Playing Track finds the right file for the song from the system media session and moves with seek and pause. Exact Match or Partial Match for small spelling differences; timed lyrics from the player are used when the library has no match. Files open in a tall editor with colouring for timestamps, word times and tags; removing one asks first. Nothing is fetched from the network.
- Logo — an image placed anywhere in the frame, automatically sized, with size, opacity, glow, position and audio pulse; animated GIFs play. The logo can also show the album cover of the playing track. Applying a template keeps your logo and only changes its placement.
- Logo and video libraries 3.1.5 — imported files are copied into the app's own folder and referenced by id, so the settings file stays small and a moved original does not break a scene.
- Visual objects — image sprites in front of or behind the visualizer, with count, size, drift, rotation and audio response.
- Media layer — a webcam or a video file, in front of or behind the visualizer, with fit (cover,
contain, stretch), mirror, kaleidoscope with 3–12 slices, hue shift, saturation, blend mode,
opacity, and audio-driven zoom and opacity. The same frame is readable inside Studio shaders as
sv_media. - Smooth on every output 3.1.5 — videos are decoded on the CPU by default, so they play at full frame rate in full-screen windows and in the Spout/Syphon feed. With hardware decoding, Chromium produced almost no frames there (0.3 per second, measured) and the clip froze at its loop point. HEVC/H.265 videos need Settings → Application → Hardware Video Decoding (applies after a restart); the panel says so when such a file will not open.
Timeline. Lay scenes and setting changes out along time, aligned to the bar or to the second; one playhead drives every screen together, and offline export plays the same show.
- Clip tracks for scenes, templates, palettes, video, images, shaders and actions; automation lanes for any setting, with curves; markers and a loop.
- A full editor 3.1.5, modelled on Ableton's Arrangement View: a toolbar with transport, clock (time and bar.beat), tempo, snap, loop, follow and zoom; track headers with colour, mute, solo and lock; split at the playhead, duplicate, copy and paste, nudge, 100-step undo; multi-select with box selection; a draggable loop brace and marker flags; per-clip transition handles; tempo changes on the ruler; adjustable lane height and a full-window mode; and keyboard shortcuts that only act while the editor has focus.
Clip Deck. Place scenes, templates, palettes and media on a grid and fire them on the beat.
- Beat-quantised launching with a global quantise setting, follow actions (next, random, go-to, loop, stop) and durations.
- Launch modes 3.1.5 — trigger, toggle and gate (plays while held).
- Several decks with tabs, named rows and columns, colours, progress bars, a blinking countdown on queued cells, drag-and-drop to move or copy, and a Performance View for a second screen.
- Keyboard play 3.1.5 — 1-9 or arrows pick the row, A-P fires a slot, Enter launches the row.
- Media and action slots 3.1.5 — video, image and shader slots apply to a chosen layer; action slots run the same actions as MIDI and OSC mappings.
- Fired slots can be recorded to the timeline, so an improvised set becomes an editable show.
Tempo and Auto VJ. The track's tempo is detected and Auto VJ changes scenes, visualizer modes or colours by itself, aligned to the bar. Pick exactly which scenes, modes or palettes cycle (or leave it empty for all), give each visualizer layer its own mode, and read a status line that says what changed, what comes next, and why nothing can happen when a source is empty.
MIDI. Learn a control, then map any CC or note to any setting or action — including MilkDrop's next, previous, random, cut, lock and rating.
OSC. A UDP listener with a hand-written OSC 1.0 parser, for TouchOSC, Resolume, Ableton or QLab.
Phone remote. A phone-sized page for scenes, colour palettes, Studio presets, visualizer and background modes, audio sensitivity, blackout and the now-playing card, served by the same server as the OBS overlay. Values sent from the phone are range-checked before they are applied 3.1.5.
Blackout on Space or from any controller, with its own transition, leaving the stored scene untouched.
Multi-monitor. A separate full-screen window on every display you select. The chosen displays are remembered by position and size, so a monitor that Windows renumbers after reconnecting is found again 3.1.5.
Floating window 3.1.5. A picture-in-picture window with opacity, a 16:9 aspect lock, a position lock and click-through, for keeping the visuals over your desktop while you work.
Streaming output — OBS and the browser. Turn on Output → Streaming Output and the application serves an overlay page.
- Add it to OBS as a Browser Source. No plugin, and real transparency.
- The overlay runs the same engine as the desktop window, so what you see is what streams — MilkDrop presets and their textures included.
- Works across the network, so the visualizer can run on one machine and OBS on another.
- Transparency is the app's own switch; add
?transparent=0or?transparent=1to one source's address to force it either way. - Lyrics follow the app, and the browser corrects its clock to the machine running the app.
- A diagnostics card 3.1.5 — add
?debug=1to the address to see the connection, configuration, audio frame rate, canvas size, transparency, app version and last error. - Access is protected by tokens; the overlay and the phone remote have separate ones.
GPU output — Spout and Syphon. The picture can be handed to another application on the same machine over the GPU: no window capture, no CPU copy.
- Spout on Windows, Syphon on macOS. Receivers include Resolume, OBS, TouchDesigner and MadMapper — anything that speaks either protocol.
- Pick the source name, resolution and frame rate.
- It renders in its own hidden window, so the feed keeps running even when no visualization window is open.
- Spout and Syphon stay opaque: a shared GPU texture cannot carry alpha without dropping the sender.
- Not available on Linux, which has no built-in equivalent. The panel says so and points at the OBS browser source, which works everywhere.
Projection mapping
- Corner pin as a true homography, with the denominator written into
gl_Position.wso the texture stays perspective-correct. - Mesh warp on a Catmull-Rom grid that passes through its control points.
- Soft edge blending for multi-projector rigs; the curves are tested to sum to exactly 1 across the overlap.
- Per-output crop, colour correction and Bézier polygon masks, plus alignment grids, crosses, colour bars and focus rings, with drag, arrow-key nudge and exact numeric entry.
Aspect correction. A display's reported resolution does not always match its physical shape. A panel driven at 1920×1080 that is really about 3:1 — a stage LED wall, a bar display, an anamorphic projector, a TV forced into a stretched mode — draws every circle as an ellipse.
- The frame is never stretched. The scene is drawn on a square-pixel canvas matching the panel's real shape and squeezed linearly into the framebuffer, where the panel's own distortion undoes the squeeze. Nothing is cropped and nothing is pushed off the edge.
- One setting corrects everything — background, visualizer, logo, text and sprites together.
- Calibrated by eye with a circle, square or grid, or from the panel size, the true aspect ratio or the dimensions of an already-stretched image.
- Per screen or all screens, composes with projection mapping without moving an existing corner calibration, and leaves exported video, the stream and the web overlay alone.
Windows Dynamic Lighting
- Off by default, and available only when compatible devices are detected.
- Dynamic modes: visualizer colour flow, bar-spectrum mapping, bass/mid/treble zones, background-light sync, synchronised beat flashes, frequency ripples, bar and background fusion, cross-device colour flow, rainbow flow, and threshold-triggered bursts.
- Manual modes: one colour, per-device colours, and per-LED or per-zone colours where the hardware exposes them.
- Brightness, audio reactivity, smoothing, update rate, LED layout, palette source, per-band colours and sensitivity, flash threshold, strength and decay, ripple speed, direction and width, and colour spread are all configurable.
- The installer registers the Windows background-lighting identity, so lights keep running when the app is not focused. Place the app near the top of Dynamic Lighting → Background light control.
OpenRGB — RGB everywhere else. Talks to a running OpenRGB server over its own protocol (TCP, port 6742 by default) on Windows, macOS and Linux — no vendor software, and the server may sit on another machine. Every device OpenRGB exposes, per-LED where the hardware allows, with the same modes and colour maths as Dynamic Lighting.
Art-Net / DMX. ArtDMX output to fixtures and lighting desks; the packet layout is tested byte by byte.
Colour sources. The lights can follow the background, the theme, or the live MilkDrop picture 3.1.5 — sampled about 30 times a second into eight slices from left to right, so the fixtures on the left take the colours on the left of the screen.
- Offline video export (audio file → MP4). Renders a track frame by frame — not a screen recording — at 720p, 1080p, 1440p or 4K, 30 or 60 fps, with quality, a CPU or GPU encoder, progress, cancellation and a GPU-to-CPU fallback. It is deterministic: the same job gives the same video, the property the visual regression tests rely on. Title, artist, album and cover are read from the file and feed Now Playing and text layers.
- Live recording of the output exactly as it appears — with the live audio, modulation, transitions and effects — to MP4 or WebM, on one key.
- GIF export with two-pass palette generation, because one pass bands visibly.
- PNG snapshot at up to 4×, on a shortcut.
- Export presets for common aspect ratios.
3.1.5
An agent drives the running application over the Model Context Protocol. The switch is on the Control card and is off by default. The application stays open while the switch is on. The setup dialog gives a stdio command for Claude Desktop, Codex, Cursor, Grok and Grok Bot. Ollama is a local model behind an MCP client and uses that same command.
Needs Node.js. The stdio command runs
node, so the computer needs Node.js LTS installed and onPATH. The application itself does not need it. Without Node.js the MCP client reports that it cannot startnode.
The server speaks JSON-RPC initialize, ping, tools/list and tools/call. It accepts protocol
versions 2024-11-05, 2025-03-26 and 2025-06-18, and answers 2024-11-05 for any other
version. The server name is soundvisualizer. There are 96 tools.
The client spawns the stdio bridge. The bridge posts to http://127.0.0.1:<port>/mcp with a bearer
token. The socket binds 127.0.0.1 only. The port is 38471 unless you set another. Port
8722 is refused, because that port belongs to the stream. A busy port stays busy: the server
does not move, and the card reports the failure. Each start writes a new token into
mcp-endpoint.json in the application data folder, next to a copy of the bridge. On Linux and
macOS those two files are readable by this user only. A connection from anywhere else is refused,
and so is a missing or wrong token.
sv_get_config and the output-status read replace stream tokens with a redaction. sv_set_stream
drops token and remoteToken. A full-config sv_export_json writes the settings as stored, to a
path you name.
Five modes stack. The switch turns on in Read. A higher mode includes the ones below it. The
agent cannot raise its own access: sv_patch_config refuses any mcp.* path. A blocked call names
the mode it needs and tells the agent to leave the mode alone and leave the panel unclicked.
sv_list_permissions and mcp_permissions report the active mode and, when you pass a tool name,
the minimum that tool needs.
A change the mode allows is saved and pushed the same way a click is. The admin panel, open visualizer windows and the stream all receive it. After every call, success or error, the preset folder is re-read and open windows receive the delta.
The master switch is enough. These calls only read.
sv_get_statereturns the show: active preset, scenes, layers, effects that are on, displays, BPM and levels, now playing, the layer stack, stream status, Spout/Syphon status and the live analysis from the panel preview (empty while the preview has no sound).sv_get_visual_state,sv_list_layers,sv_get_layerandsv_get_layer_stackreturn layer position, settings and per-layer effects.sv_get_previewadds a JPEG of the picture on screen, at most 480 pixels wide. It uses an open visualizer window, then the floating window, then the preview rectangle in the admin panel.sv_get_audioreturns level, bass, mid, treble, BPM and confidence from the same meter the panel draws.sv_get_now_playingreturns the current track.sv_list_audio_sourceslists the configured inputs.sv_list_scenesandsv_get_sceneread saved scenes.sv_list_modeslists every visualizer and background id, layer kind and blend mode; the mode and layer tools reject an id it does not list.sv_list_effectslists the global chain, each layer's chain, and the 40 built-in effect types.sv_list_presetslists library presets and user colour palettes.sv_list_displayslists screens.sv_get_output_statusreads which visualizer windows are open, the stream switch, port and LAN flag, and the Spout/Syphon name.sv_get_timeline,sv_get_clipdeckandsv_get_autovjread those panels.sv_get_configreads the whole configuration or one dotted path.
Uses what is already saved.
sv_apply_sceneloads a saved scene by id, or by name when that name is unique. The snapshot covers background, visualizer, layers, groups, crossfade, geometry, effects, logo, images, media, text, modulation, transition, Studio, MilkDrop and feedback. Window transparency and taskbar cover stay as they were.sv_apply_templateapplies a built-in template by id or name.sv_set_visualizer_typeandsv_set_background_typeswitch to an existing mode id. A Studio shader is shown with typecustomand itspresetId.sv_set_layer_enabledshows or hides a layer and turns the stack on.sv_set_crossfadesets the A/B fader from 0 to 1.sv_trigger_clipfires one clip-deck slot by row and column.sv_stop_clipsstops every playing slot. The grid stays as saved. Both need the admin window open.sv_set_effect_enabledandsv_set_effect_paramchange a global effect already on the chain. The layer pair does the same for one layer.sv_set_modulation_enabledturns the modulation matrix on or off.sv_set_macrosets one existing macro fader.sv_load_presetcopies an existing library preset into the live MilkDrop source.sv_apply_color_presetpaints an existing user or built-in palette onto the background gradient.sv_set_milkdrop_cyclesets how the library advances: auto next, order, source, tag, unit, bar count, track advance and hard cut.
Creates and edits.
- Scenes:
sv_create_scenestores the current look under a new name,sv_update_sceneoverwrites one,sv_rename_scenerenames,sv_delete_sceneremoves. - Layers:
sv_add_layer,sv_update_layer,sv_set_layer_position,sv_set_layer_settings,sv_remove_layer,sv_reorder_layers. A layer carries kind, type, preset, opacity, blend, transform (x, y, scale, rotate, flip), audio response, mask, solo, mute, lock and group. Effects on a layer go through the effect tools. Adding or showing a layer turns the stack on. sv_set_textedits the text overlay, including a lyrics or now-playing source.sv_set_logo,sv_set_mediaandsv_set_geometryedit those blocks.- Effects:
sv_add_effectandsv_remove_effecton the global chain,sv_add_layer_effectandsv_remove_layer_effecton one layer. The built-in types are bloom, chroma, glitch, grain, crt, pixelate, kaleido, mirror, grade, vignette, trails, edge, zoomblur, ripple, posterize, blur, radialblur, motionblur, tiltshift, dof, sharpen, emboss, dither, halftone, ascii, hatch, paint, vhs, datamosh, slitscan, lens, twirl, polar, gradientmap, levels, threshold, solarize, godrays, badtv and starfilter.sv_add_modulation_routeandsv_remove_modulation_routeedit routes. - Presets:
sv_save_presetwrites a file in the preset store. Shader text with no kind is saved as a Studio visualizer (kindvisualizer,engineshader). Any other save with no kind is MilkDrop.sv_delete_presetremoves a file.sv_set_milkdrop_sourcewrites MilkDrop source into the live show.sv_create_color_presetsaves a user palette of at least two colours.sv_delete_color_presetremoves a user palette. - Auto VJ:
sv_set_autovjsets enabled, source, interval, unit, order, BPM lock, palette source and per-layer visualizer targets. - Export and recording:
sv_start_exportrenders an audio file that exists on disk to a video path you name. Resolution, 30 or 60 frames per second, a CPU or GPU encoder, speed and quality are the same options as the export panel.sv_cancel_exportstops a running export.sv_export_jsonwrites the scene list, or the full settings, to a path, with no dialog.sv_save_snapshotwrites the live picture to a path as a JPEG. These three tools take only an absolute local path with the matching extension:.mp4for the video,.jsonfor the settings,.jpgor.jpegfor the picture. Network paths and addresses such astcp://are refused.sv_record_startandsv_record_stopdrive the admin recorder. Stopping opens the same save dialog as the Record card. The recorder needs the admin window open.
Opens the live surfaces.
sv_open_outputopens the visualizer on the chosen displays. Passing a display id replaces the selected set.sv_close_outputcloses visualizer windows.sv_set_displayschooses displays and leaves the windows as they are.sv_set_streamchanges the OBS and browser stream.sv_set_texture_sharechanges Spout and Syphon.sv_set_aspectchanges aspect correction.sv_set_powerchanges the frame-rate cap, the render scale andkeepAwake, which keeps the display awake while a visualizer window is open.sv_set_floatingchanges floating-window preferences, including opacity and click-through.sv_set_floating_openopens or closes that same picture-in-picture window.sv_set_window_modesets a transparent background, the transparency threshold and taskbar cover.sv_set_lightingchanges Windows Dynamic Lighting.sv_set_openrgbchanges OpenRGB.sv_set_artnetchanges Art-Net.sv_set_audio_sourcesreplaces the input mix.sv_set_mappingwrites one display's projection map: enable, corners, crop, edge blend, masks, mesh, colour and test pattern, and turns mapping on. It binds no new network port.sv_timeline_transportplays, pauses, stops or seeks the timeline through the admin transport. The admin window has to be open.sv_set_blackouttakeson,offortoggleand leaves the stored scene in place.sv_set_blackout_transitionsets the blackout transition type and duration.
sv_patch_config sets any other dotted path. The mode follows the path: scene content, effects
and presets need Write; export paths need Write; displays, stream, lighting, mapping,
windows and the timeline need Full; control.* (MIDI and OSC bindings) needs Everything.
A path that holds a stricter one needs that mode too: the whole stream object needs
Everything because it holds the tokens, and the whole power, audio, background and
transition objects need Full. A path the table does not know needs Everything. The paths
mcp.*, version, __proto__, prototype and constructor are refused.
sv_updates_download and sv_updates_install download and install an update the app has found.
sv_rotate_stream_token replaces the OBS or remote token. sv_repair_audio restarts capture when
the audio component is healthy; when it is missing it says so, because installing it needs the
user's consent. All four need Everything. The reads sv_diagnose_audio and sv_get_analysis
return the capture diagnosis and the live analysis (key, chord, pitch, loudness, drum bands).
- Eight categories — Scene, Audio, Lighting, Output, Control, Studio, Library and Settings.
- Live everywhere — every change reaches the output windows immediately and saves itself.
- A live preview with its own demo signal, audio meters and saved scenes beside every page.
- Modified badges on each card and category, with a reset for the section or the whole category.
- Search across every setting in every category (Ctrl + K).
- Advanced toggles hide rarely used controls until you want them; extended ranges raise the slider limits 5×.
- Turkish and English, switchable at runtime; the self-test fails if any interface string is left untranslated.
- Accidental-close protection — a visualizer window that closes unexpectedly (a crash, Alt+F4) reopens at once; Prevent accidental quit asks before the app closes during a show; an optional ESC lock, with Ctrl + Shift + Q as the way out.
- Keeps the display awake 3.1.5 — on by default while a visualizer window is open and not minimised, on Windows, macOS and Linux; minimising or closing hands control back to your power settings.
- Every GPU surface recovers from a lost context 3.1.5 — a driver reset no longer leaves black layers until a restart.
- Main-process faults are contained 3.1.5 — an unexpected error is logged and shown in the panel instead of freezing the app behind an error dialog.
- Two copies at once 3.1.5 — a second copy names the one already running and offers to switch to it; a copy never silently writes over settings someone else changed.
- Updates 3.1.5 — Library → Updates checks GitHub Releases, shows the notes, and lets you skip a version. The Windows installer and the AppImage download and install in place; nothing runs unless the size and the SHA-256 match exactly. Off, notify (default) or automatic.
- Settings backup and restore — every setting in one JSON file; palettes and scenes have their own export and survive an import. Files written by 1.3 and 2.0 load without losing a value.
- Power and performance — Match Display frame rate or a cap of 120, 60 or 30 FPS, background resolution scale, pause on silence, hide cursor, always on top, and cover the taskbar.
- No account, no telemetry, no analytics. Settings live in your user folder.
- One network call of its own: GitHub's latest-release API for the update check, with no identifiers. Set updates to Off and there are none.
- Everything else is opt-in and local: the stream server (LAN, token-protected; it refuses
pages from other origins and host names it does not know 3.1.5), OSC, OpenRGB, Art-Net
and MCP (bound to
127.0.0.1only, with a bearer token). - Converters and generators run offline — Shadertoy and ISF import, the scene generator, the MilkDrop preset generator, lyrics.
- The camera is never opened by automation, and a fuzzing test asserts that no MilkDrop preset can smuggle JavaScript into the engine.
Is it free? Yes. CAYADEV Visualizer is open source under the MIT licence. There is no paid tier, no account and no watermark.
Do I need Stereo Mix or a virtual audio cable? Not on Windows: system audio is captured straight from the output device. On macOS, system audio needs a virtual device such as BlackHole (microphones work directly). On Linux, the PulseAudio or PipeWire monitor source is used automatically.
Does it work with Spotify, YouTube, Apple Music or a DAW? Yes — it visualizes whatever your computer plays. On Windows you can capture one application only (say, Spotify) and ignore everything else, and Now Playing reads the track title and cover from the system media session.
Can I use it as an OBS overlay? Yes. Turn on Streaming Output and add the address as a Browser Source. Transparency works, and OBS can run on a different computer on the same network. Spout (Windows) and Syphon (macOS) are there for GPU sharing as well.
Can it load my MilkDrop presets?
Yes: single .milk files, MilkDrop 3 .milk2 double presets 3.1.5, ZIP packs, folders, or
a search of your computer. Point the app at a MilkDrop textures folder for presets that use images.
No third-party preset pack is bundled; five original presets are.
Is it a Winamp or projectM plugin? No. It is a standalone application with its own MilkDrop-compatible engine in WebGL2. It does not embed projectM or MilkDrop.
Does it support several monitors and projectors? Yes — a full-screen window on every selected display, plus projection mapping, edge blending and aspect correction for projectors and LED walls.
Can I make a music video from a track? Yes. Video Export renders an audio file to MP4 frame by frame, up to 4K at 60 fps, using the current scene. It is not a screen recording, so the result does not depend on how fast your computer is.
Can AI agents control it? Yes 3.1.5. Turn on MCP on the Control page and connect Claude Desktop, Codex, Cursor or any MCP client with the command the setup dialog gives you. Access starts at read-only. The command needs Node.js LTS on the computer.
Will it slow my computer down? It needs a GPU with WebGL2. Use the frame-rate cap, the background resolution scale and pause on silence on laptops; the MilkDrop panel shows mesh density and internal resolution.
Can it be a screen saver? There is a Screensaver template group, and the display is kept awake while the visuals run, but the application does not register itself as an operating-system screen saver.
How well are macOS and Linux supported? Both are built on CI runners and the audio engine loads there, but neither build has yet been run on real hardware by the project. Windows is where everything is measured. Reports from Mac and Linux users are very welcome.
My video freezes in full screen or in Spout, or an iPhone video will not play. Videos are decoded on the CPU by default, which keeps them smooth in full screen and in the Spout/Syphon feed 3.1.5. HEVC/H.265 files (common from phones) can only be decoded in hardware: turn on Settings → Application → Hardware Video Decoding and restart the app.
Is my data sent anywhere? No. See Privacy and security.
The engine's fidelity work, measured preset by preset. MilkDrop Fidelity is on by default; switching it off restores the engine's earlier look.
- The preset language actually runs — tokeniser, parser, and compilation to JavaScript
closures.
per_frameandper_pixelequations drive a real warp mesh with feedback, includingmegabuf/gmegabuf,loop,while,exec2/exec3and compound assignment. - Measured against 10,347 real presets from the projectM original and cream-of-the-crop packs: every one loads and runs, 10,344 of them with no skipped statement at all.
.milkimport, including multi-file packs with per-file compile errors reported.- Five presets of our own ship with the application. Kutup Işığı (a flowing nebula), Erimiş Altın (the loud one), Dingin Halkalar (slow and nearly black), Sonsuz Tünel (the classic tunnel) and Nabız Örgüsü (squares on the beat over a motion-vector weave) — written here, so the engine shows what it does before you import a pack of your own. They join auto advance like any other preset. The two pictures on this page are rendered from them.
- No preset text is copied into generated code. There is no
evaland no generated source: each node becomes a closure, identifiers become pool indices, and function names are resolved against a fixed table at compile time. - The HLSL warp and composite shaders are translated to GLSL and run on the GPU. Measured in a
real WebGL2 context over the 10,332-preset corpus: all 16,346 shader stages compile, and every
one of the 8,485 presets that carries a shader has every stage clean. A separate harness renders each preset and reads the pixels back,
because a shader that compiles can still draw black: ~98% produce a live image. The seeded slice resolves to about a preset either way: the harness deliberately carries the feedback buffer between presets, so one preset sitting on a class threshold can move the last digit without anything in the engine changing. Both
harnesses are in
scripts/, so the numbers can be reproduced rather than believed. - The picture uses MilkDrop's own values, not an approximation of them. Each of these was found
by diffing the corpus against what the engine actually reads, and each is measured: two header
settings never reached the equations at all (
fZoomExponentandfWaveParam, one of which 66.6% of presets rely on); the warp mesh ran in a vertically mirrored space, sody,cyand the direction of rotation were flipped (48.8% usedyorcy); the warp ripple used invented constants and ignoredfWarpScaleandfWarpAnimSpeed(79.9% and 44.0%); the waveform ignoredfWaveSmoothing(79.0%), a custom wave's ownsmoothing, andbModWaveAlphaByVolume(38.9%); and the outer/inner borders (37.8%) and the centre darkening (6.9%) were never drawn at all. A MilkDrop Fidelity switch restores the engine's earlier look. - The mesh transform is MilkDrop's own, read from MilkDrop 2's source.
radis left unnormalised as MilkDrop leaves it,angkeeps its(-π, π]range with the centre node pinned, aspect is applied at the start of the transform and undone at the end so a circle stays a circle on a wide screen, and the steps run in MilkDrop's order — zoom about the centre of the screen, then stretch, warp, rotate, translate.aspectxandaspectywere swapped, so a preset correcting for a wide screen corrected the wrong axis. The blurred copy is darkened at its edges (b1ed, 68.6% ask for it), and a custom wave is drawn at MilkDrop's amplitude and reads the spectrum when it asks for the spectrum (23.2% do). - Presets without a shader of their own look as they do in MilkDrop 2. MilkDrop picks a preset's
warp and composite shaders by the file's version, not by whether shader text is present, and
draws the rest with a fixed pipeline built from blend passes. Checked against MilkDrop 2's source
and the original D3D9 code: brighten there is
1−(1−c)²and solarize2c(1−c), not thesqrt(c)and4c(1−c)the engine used (410 and 83 presets of a 10,332-preset corpus turn them on); echo orientation is(int)x % 4; gamma below 1 is ignored while echo is on; and when two presets whose echoes point different ways blend, the echo fades out and back in instead of flipping. A preset that leaves out its decay or gamma gets MilkDrop's 0.98 and 2.0 instead of 0, and so do the other values it leaves out — down to MilkDrop's quirk of reading a missing wave colour as 0; a rotation centre of 0 stays in the corner. The values MilkDrop keeps out of the equations (wave scale and smoothing, the volume fade, warp speed and scale) come from the file as they do there, so a wave scale of 0 flattens the wave (102 presets of the corpus). Rendered through the engine, every case matches MilkDrop's formula within 2/255. - Preset files are read the way MilkDrop reads them. With fidelity on, keys are case-sensitive,
a key written twice takes the value MilkDrop finds (usually the first), numbered code ends at the
first missing number, integer settings drop their fractions, and equation lines are glued the way
MilkDrop glues them,
\\comments included. 32 presets of the corpus read differently; with fidelity off the old parser stays. - Equations run by MilkDrop 2's compiler rules. With fidelity on, truth and equality tests use
its 0.00001 tolerance,
%works on whole numbers without sign,&and|on 64-bit integers,megabufandgmegabufindices are rounded and bounded the way it does,rand(n)returns a real number (5,361 corpus presets call it), and the compiler's internal names such as_aboeqrun. With fidelity off nothing changes. - Each block keeps MilkDrop's own variables. With fidelity on, per-pixel code and a wave's
per-point code run in their own variable space the way MilkDrop's separate machines do: they see
only what MilkDrop hands them (time, audio, the mesh size, q1..q32 and for waves t1..t8), keep
their own variables and
megabuf, and nothing they write leaks back.reg00..reg99are shared by every block and preset, so shapes now see the random values init leaves there, andloop/whilestop at MilkDrop's 1,048,576 turns a call. - Composite shaders get the hue colour MilkDrop gives them. MilkDrop passes every composite
shader four slowly drifting corner colours as
hue_shader, whatever the preset'sfShadersays;fShaderonly scales them on the fixed pipeline. The engine appliedfShaderto shaders too, so about 950 presets of the corpus drew without their colour or with part of it, and the corners were mirrored top to bottom. On the fixed pipeline the colour now goes through MilkDrop's own draw passes, the wraparound of out-of-range colours included. - Textured shapes, motion vectors, rotation matrices, mesh density, internal resolution scale,
mouse input and preset transitions are all implemented, and each sampler is read with the
filtering and wrapping its name asks for (
sampler_pw_mainis point-sampled,sampler_fc_mainis filtered and clamped — 22.7% of presets read one texture through two different prefixes). - User textures load from your own texture folder. 16.9% of presets ask for an image by name —
sampler_wormslooks forworms.jpg. Preset packs do not ship these files, so point MilkDrop › Texture Pack at thetexturesfolder of a MilkDrop installation. Without one the preset still runs, with noise in place of that texture. The textures load wherever the preset is drawn: the visualizer windows, the panel's live preview, the web overlay — which fetches each image by name from the stream server, behind its token, and is sent a short digest of the folder rather than its path — and video export, which waits for a texture before drawing the next frame, so the same job still gives the same video. - Flash limiting holds on every screen. On by default, it limits how fast the picture's mean brightness may change, at WCAG 2.3.1's general-flash value: 0.10 of relative luminance per frame at 30 fps, the frame step it was measured at. The limit used to be per frame, so a faster display let flashes through faster — measured with a preset that flips between black and white three times a second, the swing per cycle was 0.714 in the panel's 45 fps preview but 1.000 on a 74 Hz display, the flash untouched. It is now held per second: 0.43–0.49 in the preview, in 60 Hz and 74 Hz windows, in a 35 fps window and on the web overlay alike.
- MilkDrop follows the system's reduce-motion setting. When the operating system asks for reduced motion (on Windows, Animation effects turned off under Accessibility › Visual effects), the flash limiter stays on even if it was turned off, loud moments no longer hard-cut, and every change blends over 5 seconds. Auto advance plans with that longer blend, so a preset is still shown in full for the time you set. A manual Cut now still cuts. The MilkDrop panel says whether it is on and why, and Reduce Motion overrides it either way: Always, or Off even when the system asks. Each screen reads its own system — a web overlay on another machine follows that machine — and video export follows only Always, so a video never depends on the machine that made it. Checked in an isolated copy with the setting emulated in the browser engine; the system's own setting was left alone.
- A lost GPU context comes back. A driver reset or a GPU process crash takes every WebGL object with it, and MilkDrop stayed black until the application was restarted: nothing in the code listened for it. The engine now holds the loss, asks the browser for the context back and rebuilds its programs, textures and buffers on the same canvas when it arrives; if it does not arrive within three seconds — a browser that gave up, or a context lost by hand — it starts again on a fresh canvas. The running preset survives either way: the same object, the same equation state and the same clock, so it carries on from where it stopped instead of restarting. What cannot come back is the feedback buffer's content, which lived in GPU memory, so the picture flows again from black. Chromium's habit of blocking 3D for a page whose GPU process crashed is turned off, because that block would leave the recovery with nowhere to go. The GPU self-test loses the context on the running engine both ways and checks that frames, pixels and the preset's own state come back.
- So does every other GPU surface. A real GPU reset takes every context at once, so MilkDrop coming back was not enough while the gradient background, the 3D geometry mode, the shader modes, the effect chains and projection mapping stayed black. None of them carries feedback or built-up state, so each now reports a lost context and its owner builds a fresh one with the same settings: a layer is rebuilt in place, in the same position and with the same blend, opacity and z-order; the effect chain keeps its effects. A surface is rebuilt at most every two seconds, so a GPU that is still restarting does not get a new canvas every frame. The GPU self-test loses the context of a gradient background and of the effect chain on the running stage and checks both come back with a picture (brightest sample 175 and 206 of 255, the same as before the loss).
- Preset transitions are MilkDrop's dual pipeline. The previous preset does not stop when a new one loads: it keeps its own object, its own compiled shaders and its own clock, and both presets run their frame and vertex equations every frame. The two warp meshes are blended per node along a ramp — a directional wipe, a plasma or a radial sweep, picked at random as MilkDrop picks it — so one part of the screen turns over before another, and the same ramp is the per-node alpha the two presets' shaders are drawn with. There is still one feedback buffer and one blur chain, which is what MilkDrop has. Values that do not move pixels (decay, wave and border colours, blur ranges, gamma) are blended numerically on a cosine curve; the boolean ones snap. Approximate: if the two presets use different waveform modes, MilkDrop morphs one shape into the other; here the new preset's mode is shown for the transition.
- What a transition costs, measured. Running two presets is close to exactly twice the work, so
the default is only defensible with a number behind it. At 1280×720 against a 60 fps budget of
16.67 ms, the median frame goes 2.70 ms → 5.20 ms at the default mesh of 64 (31% of the
budget), 0.90 → 1.70 ms at mesh 32, and 5.80 → 11.10 ms at the densest mesh of 96 (67%).
Separately, the first frame of any preset change compiles the new preset's shaders — 9.10 ms at
mesh 64 with a hard cut, 12.10 ms with a transition; at mesh 96 that one frame goes 12.00 → 18.00
ms and drops a frame. So the default is 1.7s, MilkDrop's own
fBlendTimeUser, and automatic preset advance stays off by default, which means a transition only ever runs when you ask for one. - A preset change no longer stalls the frame. Measured across 900 presets at 1280×720, the
frame that loaded a preset ran a median 17 ms and at worst ~119 ms over its neighbours, and six
changes in ten dropped a frame that would otherwise have held — nine tenths of it the GPU compiling
the new shaders while the frame waited. Where the driver offers
KHR_parallel_shader_compilethe compile now runs in the background and the running preset keeps drawing; the change, transition included, starts once the new programs are ready, a median two or three frames later. With auto advance the next preset is chosen a second early and compiled in advance, so the change still lands on time — on the bar in bar mode — and the other screens prepare the same one. At the default mesh the changes that drop a frame fell from 61.9% to 4.9% with a hard cut and from 61.7% to 8.1% with a transition. Video export still waits for each compile, so the frame a preset appears on never depends on the machine. - A new track can bring the next preset. Next to auto advance, On Track Change moves to the next preset — in the chosen order, random included — when Now Playing sees a new track. It works with the timer off, the lock stops it too, and only the leading screen picks, so every screen changes together. The track already playing when the app opens does not count, nor does the gap between two tracks.
- Favourites, tags and search by author. Star presets in the MilkDrop panel's list, or the one
on screen with Favourite (also from a MIDI or OSC mapping), and give them tags of your own.
Search matches the name, the author and the tags. MilkDrop names are mostly "Author - Title", so
author:geiss(oryazar:) finds Geiss's presets but not a title that mentions him, and#calmfinds a tag. Show narrows the list to favourites or one tag, Author to one author. Pool limits auto advance — the timer, hard cuts and track changes — to favourites or one tag, while Previous, Next, Random and the list still reach every preset. Favourites and tags live in the settings next to your ratings, not in the preset files, and a pack carries all three: Pack What Is Shown writes the presets in view with them, and importing the pack attaches them to the new copies. Checked in an isolated copy: with the pool set to three favourites, the visualizer window went round those three and nothing else. - Large preset libraries stay light. Every save or delete used to re-read every preset file and send the whole list, sources included, to the panel, each window and every web client. With the 10,347 presets of a full MilkDrop library (116 MB) one delete held the app for about 2.4 seconds and the panel used 631 MB. Presets are now read once and kept; a change sends only what changed. At the same size a save now takes 91 ms and the panel uses 273 MB. Deleting or adding MilkDrop presets no longer restarts the MilkDrop picture on screen. Web clients get MilkDrop presets without their sources (271 KB instead of about 30 MB for 2,000 presets), and the web overlay fetches the source of a preset only when it is about to draw it.
- Import a whole MilkDrop library. Import from ZIP Pack, Import from Folder and Search This
Computer in the MilkDrop panel bring in a library in one go: nested folders, the pack's
textures, and — if you want — the pack's category folders as tags. Nothing is copied until you
have seen what will be added (presets, textures, MB, and what will be skipped: MilkDrop 3's
.milk2double presets, oversized files, encrypted ZIP entries) and confirmed. Presets with the same name and content are skipped, so importing a pack twice adds nothing. Textures go into the app's own folder and are looked up after the texture folder you chose, so a pack'ssampler_wormsworks without choosing a folder. The search looks in the usual Winamp, foobar2000 and projectM folders and in Downloads, Desktop, Music and Documents, wherever the system keeps them, and stops after a few seconds; a library it could not finish counting is scanned in full before you are asked. No preset pack ships with the app. Measured with real packs: a 9,795-preset folder was scanned in 1.1 s and imported in 6.2 s, and importing it again added nothing. - Preset thumbnails. The MilkDrop panel's list has a Grid layout: every preset shows a small picture of itself, so a preset can be found by its look. Each thumbnail is drawn once in the background — the first two seconds of the preset with the demo sound, from a black screen — kept in the app's data, and drawn again when the preset or a texture it uses changes. Only the presets you scroll to are drawn. A visualizer window drawing MilkDrop at the same time barely noticed: none of its frames took longer than 27 ms. Found while building this: when another MilkDrop window was drawing and the computer was busy, a MilkDrop picture that had just set up its buffers (a new window, a resize, a thumbnail, a video export) could start from the other window's picture. Buffers now start from zeros.
- A preset generator of our own. Studio → MilkDrop Preset Generator writes an original
MilkDrop preset from four sliders — energy, warmth, density and motion — and a seed. It runs
offline, and every motion, wave, shape and shader pattern it uses was written for this app: no
line of it longer than 30 characters appears in a 10,332-preset corpus. The preset loads straight
away as a preview without being saved; Save to Library keeps it. Moving a slider keeps the
seed, so you get the same preset with more energy or warmer colours rather than a different one,
and a code such as
72-15-60-88-2n9cbrings the same preset back. Nothing in a generated preset flashes: its colours stay in range at any volume, and invert, solarize and brighten are never used. Measured on 200 generated presets with silence, the demo sound and a loud bass line: every shader compiled, and none went black, washed out to white, flashed more than twice a second or froze. - Mash-ups from your library. The same card builds a preset out of pieces of the presets you already have: the look (decay, echo, gamma, the main wave), the motion equations, the custom waves, the custom shapes, the warp shader and the composite shader — each taken whole from one preset and copied line for line. New Mash-up draws every part at random from the presets the MilkDrop panel's list shows, so its search and filter apply, and only from presets that have that part. A single part can be drawn again, or all six can start from the preset on screen; the arrows step back through earlier mash-ups, and Save to Library keeps one. A mash-up inherits its donors' looks, so it can come out brighter or darker than any of them. Checked on a 10,332-preset corpus: a mash-up of one preset is that preset again, and in 5,000 random mash-ups every part was exactly its donor's. 300 random mash-ups ran in the engine without a single shader failure.
- An editor for presets. Studio → MilkDrop Preset Editor opens the preset on screen: frame and
pixel equations, custom waves and shapes, the warp and composite shaders and the main values each
have a tab, and a change shows in the running picture a moment after you stop typing. Errors point
at the preset's own line (
per_frame_14) with its own text, not at generated code; shaders are compiled as you type. Sliders for zoom, warp, rotation, decay and echo say when the preset's equations rewrite the value every frame. The original is never changed: Save as a New Preset keeps your version, and only what you changed is written — the rest of the file keeps its bytes. - MilkDrop 2 and MilkDrop 3 rules. A Preset Format setting — Automatic, MilkDrop 2 or
MilkDrop 3 — picks whose file rules a preset is read with: MilkDrop 3's 16 custom waves and shapes
and q1–q64, or MilkDrop 2's four and q1–q32. Automatic reads a preset with MilkDrop 3 rules when
it uses those extensions, and the panel names them. MilkDrop 3's hard-cut modes 1–6 are offered
with its thresholds and delays. Its
.milk2double presets load as a frozen blend of their two presets. Its new waveforms, new transitions andget_fftin shaders are not here yet: nothing describes how they behave. - Sprites from
milk_img.ini. MilkDrop 2 draws images of your own over the picture during a show: each one is defined inmilk_img.iniwith an image, code that runs once and code that runs every frame, and launched by number. Choose the file in the MilkDrop panel and launch from its list, from a MIDI or OSC mapping, or with MilkDrop's own keys in the visualizer window — K and two digits launch, SHIFT+K and two digits remove that number, DELETE removes the newest. Up to 16 run at once; the five blend modes, the colour key, tiling, flipping andburn(the sprite leaves its image in the feedback and flows with the preset) follow MilkDrop 2's source. Every screen shows the same sprite,randincluded: the launch carries its seed. Measured in an isolated copy with three displays, the web output, Spout and the panel preview: all six drew the probe image at every sample point, a sprite placed withrandsat at the same x/y on all of them, and a burnt sprite's image stayed where the sprite was after it died. Sprites are a live tool and are not part of video export. - Auto advance advances. The panel's Auto Advance slider had been there since the engine landed and nothing ever read it: set to two seconds, the same preset stayed on screen (measured in the running app — nine seconds, no change). It now moves on every n seconds, in order or at random, and random never picks the preset already showing, since a "transition" to the same preset changes nothing on screen. The visualizer does the switching on its own frame clock, so it keeps going while the panel is closed or covered, and the preset it moves to is not written into the settings file, which is rewritten in full on every change. The panel's Loaded Preset row shows what is on screen, and the panel preview follows the visualizer instead of running a sequence of its own.
- Preset timing is MilkDrop 2's. Three things MilkDrop 2 does around auto advance were missing,
and now follow its source. The next change comes after the transition time, the interval and a
random extra of up to n seconds, drawn once per preset (MilkDrop's own default is 16 s plus up
to 10 s; ours adds nothing unless asked). A lock stops auto advance and hard cuts, and releasing
it carries on with the time that was left. And a hard cut — off by default, as in MilkDrop —
changes preset with no blend when bass, mid and treble, each against its own long average,
together pass three times a threshold; the threshold doubles on each cut and then falls back, so
a run of bursts is not a run of cuts. MilkDrop calls that fall-back a 60-second half-life, but its
coefficient is 2·ln 2, so the excess actually halves in 30 s; the formula is kept as it is, so a
preset cuts as often here as it does there. A preset's
progressnow means what it means in MilkDrop — how far through its scheduled life the preset is — instead of a ten-second sawtooth. 10 of the 23 corpus presets that read it useabove(progress, 0.99)to fade out just before a change, and the sawtooth made them fade every ten seconds; with auto advance off there is no scheduled change andprogressstays at 0, as it does in MilkDrop for a locked preset. - Ratings and a history, as MilkDrop keeps them. Every preset carries a rating in its own file
(
fRating, 3 when it is missing), and MilkDrop draws its random order from the cumulative distribution of those ratings: a preset rated 1 comes up a fifth as often as one rated 5, and one rated 0 never comes up on its own. Random order now works the same way — on by default, as in MilkDrop, with a switch — and the panel shows the rating you gave the preset on screen as stars you can change; a preset you have not rated shows none, while random order keeps weighting it by its own file's rating. Your rating goes into the settings rather than into the preset, because saving a preset re-sends the whole library, sources included, to every window. Previous and Next now walk the history of what was actually shown, including what auto advance and hard cuts picked, instead of stepping through the list from the preset you last clicked; the history keeps MilkDrop's 64 steps. One difference is deliberate: after going back, a new preset drops the forward part, as a browser does, where MilkDrop's auto advance would replay it — the history lives in the panel and the pick is made in the visualizer. - MilkDrop on MIDI and OSC. Controllers could drive the rest of the application but nothing in MilkDrop. They now have seven MilkDrop actions — next, previous, random, cut now (MilkDrop's H: the next preset with no blend), lock, rating up and rating down — which run the panel's own code, so the history, the rating weights and the lock behave the same from a controller; and six settings: transition time, auto-advance interval, random spread, hard-cut threshold, mesh density and internal resolution. The last two only ever take the panel's own values: a knob's travel is split into equal steps, because every value in between would rebuild the mesh or the frame buffers.
- Preset changes on the bar. Auto advance can count bars instead of seconds: every n bars, the preset changes on the first beat of a bar, and the transition is rounded to whole beats so it also ends on a beat (1.7 s at 120 BPM becomes three beats, 1.5 s). The tempo is estimated from the visualizer's own audio, not the panel's — the panel's loop stops while the visualizer covers it, which is why auto advance runs in the visualizer in the first place — and the BPM lock and tap tempo are the ones in Tempo & Auto VJ, so there is one tempo lock in the application, not two. With no tempo to be found the change falls back to time the way Auto VJ does, every twice as many seconds as bars and at least every 4 s, and the panel says so; otherwise it shows the visualizer's BPM and bar count.
- Every screen shows the same preset. With auto advance or hard cuts on, each visualizer
window, the Spout/Syphon output and the web overlay ran a sequence of its own, so random order
put a different preset on every screen — and even the same preset differed, since its four
rand_presetnumbers and the transition's pattern were drawn separately in each. Now one engine picks — the first visualizer window, else the Spout/Syphon window, else the panel preview — and the others show its pick with the same seed, so the samerand_presetand the same transition. Measured across three windows, Spout, the web overlay and the preview with random order every 2 s: all six agreed on the preset in 58 of 60 samples, the other two falling on a switch, and presets that drawrand_presetas a flat colour matched to the pixel; before, the six showed 5.7 different pictures per sample on average. MilkDrop › Displays › Each display picks its own brings back separate sequences. Randomness drawn every frame (rand_frame) still differs from screen to screen. - The lights can take MilkDrop's colors. The lights could follow the background or the theme, not what MilkDrop draws. A new color source, MilkDrop picture (live) — in Lighting › Color Source or straight from the MilkDrop panel — reads the current frame about 30 times a second: shrunk to 64×16 and cut into eight slices from left to right, each slice's color weighted by brightness so a small bright detail on a dark background still counts, its hue kept and its brightness left to the lighting mode. Dynamic Lighting, OpenRGB and Art-Net all take it, and OpenRGB now uses the sampled palette the same way Dynamic Lighting does, where it used to draw the configured gradient. Measured over Art-Net with a picture red on the left and blue on the right: the first four of eight fixtures red and the last four blue, where before all eight showed the fallback color; the window ran at 75.2 Hz with the sampling and 75.0 Hz without.
- A MilkDrop layer survives scene transitions. A scene transition used to build every layer of the arriving scene from scratch, which for MilkDrop means the preset restarting, its feedback trail vanishing and auto advance falling back to the preset you picked by hand. The dynamic colour theme changes the palette on every track and the palette counts as a scene change, so with both switched on this happened on every track — for colours MilkDrop never reads. The layer now stays in the arriving scene and the leaving one shows the same canvas through a proxy, so MilkDrop keeps running while the layers around it cross-fade. Measured in the running app, one run, the same palette change: without the fix a new instance appeared on the hand-picked preset, with it the same instance kept the preset it was showing.
- Presets hear the music the way MilkDrop hears it. MilkDrop computes
bass,mid,treband their_attversions with a chain of its own, and presets are written against how that chain behaves. We were deriving them from the visualizer's bands — smoothed twice and divided by a six-second average — and, fed the same synthetic drum track, that putbasslower just after a kick than between kicks (0.93×, where MilkDrop gives 2.45×), while the_attvalues did not rise on a hit at all. With MilkDrop Fidelity on, all six now come from MilkDrop's own chain: the newest 576 samples, a Hann-windowed 1024-point FFT with MilkDrop's equaliser, three bands summed across the lower half of the spectrum, a fast asymmetric average for_attand a four-second one to divide by. It matches an independent reference implementation to within 3.2e-6. Two differences are deliberate and measured: the averages start from the first frame that has sound in it rather than from zero (MilkDrop's own start throws every value to about 250 for a moment when the layer was created in silence), and values are capped at 30 — on the test track the music itself peaks at 11.2 and a drop after an eight-second breakdown at 18.6, so the cap only touches the first frames back from a long silence, where MilkDrop reaches 234. The Sensitivity slider still scales how far the values move away from 1: its default of 0.7 is 30% less than MilkDrop, 1 is MilkDrop. - The per-frame variables reset every frame, as MilkDrop resets them. MilkDrop re-seeds every
built-in per-frame variable from the preset file before
per_frameruns and returnsq1..q32to whatper_frame_initleft. Our pool persisted instead, soq1 = q1 + x— written by 19.5% of the corpus — grew without bound instead of giving the same answer each frame. Preset authors' own variables still persist, as they do in MilkDrop. volandvol_attare what MilkDrop hands a shader, bug included. MilkDrop's shader header makes them the fourth component of the bass/mid/treb constants, and the line that fills it reads0.3333f * (imm_rel[0], imm_rel[1], imm_rel[2])— a comma operator, so the value is a third oftreb, not the average of the three bands the engine passed. 96 presets (0.93%) read them in a shader and were tuned against MilkDrop's value. In the equations MilkDrop has novolat all, and a custom wave or shape sees only the inputs MilkDrop registers for it: the engine also copiedvol,vol_att, the mesh size, the aspect pair and the pixel size into every wave and shape. Run through the equations with the switch on and off, 19 presets (0.18%) draw a wave or shape differently, all for one reason — the per-frame code assignsvolas its own variable and a shape reads it, where MilkDrop gives that shape its own zero. Both follow the MilkDrop Fidelity switch.- A spectrum wave gets MilkDrop's spectrum at MilkDrop's scale. The
0.15multiplier in MilkDrop was chosen for the magnitude its own FFT produces, so a normalised 0..1 array draws the right shape at the wrong size. The chain is rebuilt from the source: ±128 sample units, the two-tap damping, a 576-point Hann envelope, an unnormalised 1024-point FFT and the-0.02·ln((512-i)/512)equaliser, over the newest 576 samples as MilkDrop takes them — it had been reading the oldest 576 of the 2048-sample buffer, about 30 ms behind the sound. Approximate: the bin-to-frequency axis, because our samples arrive at the AudioContext rate rather than MilkDrop's. - Waves read the newest audio, from both channels, aligned the way MilkDrop aligns them. The default and custom waves were reading the oldest 576 samples of the 2048-sample buffer, about 30 ms behind the sound, with the same mono buffer 128 samples further on standing in for the right channel. With MilkDrop Fidelity on, each channel's newest 576 samples now go through MilkDrop's alignment: the window is compared with the previous frame's, coarse to fine over six halvings, and shifted by up to 95 samples, so a steady tone stands still on screen instead of starting wherever the window happened to fall. The 96 samples past the 480 that are drawn are zeroed, as MilkDrop does, and the default wave's vertex counts start from those 480 rather than 512. A spectrum wave's two values are now the left and right spectra. A third of the corpus (33.1%) has a custom wave with more than 480 samples, which in MilkDrop reads before the start of its array; where that read lands on the other channel it is reproduced exactly, and beyond both channels it reads zero.
npm run iconsnpm run dist:winnpm run dist:mac:arm64npm run dist:linux| Platform | Output | Built on |
|---|---|---|
| Windows | CAYADEV Visualizer Setup ….exe (installer), …-portable.exe |
Windows |
| macOS | ….dmg and ….zip (the .app inside) — Apple Silicon |
macOS |
| Linux | ….AppImage and ….deb — x64 |
Linux |
Every platform builds on itself. audify is a native module and cannot be cross-compiled: a
macOS package produced on Windows shows the interface but captures no audio. The GitHub Actions
workflow therefore builds macOS on macos-latest and Linux on ubuntu-latest. Windows is built
locally instead of in CI, because the installer registers the Dynamic Lighting identity and that
needs a certificate the runner does not have — a CI-built installer would be a different product.
macOS packages are unsigned and not notarised. macOS quarantines unsigned downloads and reports them as damaged and can’t be opened; right-clicking and choosing Open does not clear that on macOS 15 or later. Drag the app to Applications and remove the flag once:
xattr -dr com.apple.quarantine "/Applications/CAYADEV Visualizer.app"It then opens normally every time. Capturing system audio there also needs a virtual device such as BlackHole.
Linux needs PulseAudio or PipeWire. The .deb declares libpulse0 among its dependencies; the
AppImage expects the same library to already be present.
The description next to each file in a release's asset list is written with
gh release upload file#label. The table lives in a script:
npm run release:assets -- v3.1.3 --dir=<the folder CI artifacts were downloaded into>It looks for every expected file in dist/ and in the folders given with --dir, refuses to upload
anything unless all of them are present, and re-reads the release afterwards to confirm each label
was written. Add --dry-run to see what would be uploaded, --check to audit a published release,
and --partial when a partial upload really is intended.
npm start -- --shotsRenders every panel, scene, clip and mode sheet on this page into docs/screenshots/, in English,
from a synthetic signal. It never captures real audio, never opens the camera, never writes your
settings, and uses typed-in track details rather than whatever is playing. --only=<name> limits it
to matching files.
npm testnpm start -- --smoke2799 unit tests, all passing. They are written to check answers, not to exercise lines:
- Formulas are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor staying bounded and landing inside the view volume.
- Tempo is measured against synthetic signals of known BPM (90/120/128/140/174 → 89.8/120.4/127.9/140.0/173.7).
- Analysis is tested against signals with known answers: a known chord must come back as that chord, a 220 Hz tone as 220 Hz.
- Art-Net is verified byte by byte against the ArtDMX header.
- Config migration loads 1.3 and 2.0 settings files without losing a value.
- Fuzzing the preset and pack loaders asserts no MilkDrop preset can smuggle JavaScript.
- MCP coverage fails when a new setting or feature is added without a matching tool or path rule.
The GPU self-test runs the real application on a real GPU. It draws every registered visualizer mode and background and checks that each one builds its canvas and that shader-based modes compiled; measures that every effect and every 3D formula leaves a non-blank picture; compiles every built-in Studio shader; loses and restores the WebGL context of MilkDrop, the gradient background and the effect chain; loads the OBS overlay from the real stream server; switches the interface to English and scans for untranslated strings; and asserts that automation never opens the camera.
CI runs the unit tests on Windows and Ubuntu with Node 20 and 22 on every pull request.
src/
main/ Electron main process: windows, audio capture, IPC, stream and MCP servers
admin/ Control panel
visualizer/ Output window: layer stack, modes, effects
exporter/ Offline, deterministic video export
shared/ Engines with no DOM: spectrum, modulation, analysis, formulas,
transitions, warp, MilkDrop, templates, lyrics, timeline, clip deck
web/ OBS overlay and the phone remote
native/ Audio helpers (per-application capture, Dynamic Lighting identity)
scripts/ Build, release, MilkDrop corpus and render harnesses
tests/ Unit tests, run by `npm test`
docs/ Screenshots
Shared engines are plain arithmetic with no DOM, GPU or audio device, so their tests run in Node.
| Key | Action |
|---|---|
ESC |
Close every visualization window |
F11 |
Toggle full screen |
Space |
Blackout |
Ctrl + S |
PNG snapshot |
Ctrl + R |
Start / stop recording |
Ctrl + K |
Search all settings (panel) |
Ctrl + Shift + Q |
Close the visualizer when the ESC lock is on |
K + two digits |
Launch a MilkDrop sprite (visualizer window) |
The timeline editor and the clip deck have their own shortcuts, listed in their panels.
ROADMAP.md records what has actually shipped and what each planned release is for — Timeline and Clip Deck in v3.1.0, cross-platform builds with OpenRGB and Spout/Syphon in v3.1.1, the MilkDrop shader engine in v3.1.2, per-application audio capture and aspect correction in v3.1.3, and the streaming overlay and transparency fixes with MilkDrop fidelity in v3.1.4. Next are MilkDrop and streaming refinements in v3.1.5, a far broader and much faster video export in v3.1.6, the broadcast layout editor in v3.1.7 and redundancy with frame sync in v3.2.0. It also keeps an honest list of what is not done, and why.
MIT — see LICENSE. Copyright (c) 2026 Çağan Turgut (CaYatur) — CaYaDev.
Keywords: music visualizer · audio visualizer · sound visualizer · VJ software · MilkDrop · projectM alternative · OBS overlay · Spotify visualizer · desktop visualizer · multi-monitor · projection mapping · Spout · Syphon · WebGL · Electron · MCP
















































