Stem-separated music visualizer: drums drive the pulse, bass drives the warp, each stem gets its own visualizer layer.
Built on projectM and Demucs. Developed on WSL2; should run on any Linux with a display.
- Python 3.10+
- FFmpeg
- libprojectM 4.2+ (needs
_opengl_render_frame_fboand_set_frame_time) - Optional: NVIDIA GPU + CUDA for faster Demucs separation
- WSL2: wsl-builds simplifies deps
./wsl-stacker.sh spoddycoder dev-ai
./wsl-builder.sh media ffmpeg
./wsl-builder.sh media libprojectmWSL2 audio glitches: see microsoft/wslg#1257. Disabling systemd-timesyncd should help.
Create a virtual environment...
# using venv
python3 -m venv cleave
source cleave/bin/activate
# or using conda
conda create -n cleave python=3.10
conda activate cleaveInstall dependencies...
# install torch with CUDA support
pip install torch torchcodec --index-url https://download.pytorch.org/whl/cu130
# or install cpu version
pip install torch torchcodec --index-url https://download.pytorch.org/whl/cpu
# install the rest of the deps
pip install -r requirements.txt# make a preset_root
mkdir ~/milkdrop-presets
cd ~/milkdrop-presets
# there are thousands of community written presets to choose from...
git clone https://github.com/projectM-visualizer/presets-cream-of-the-crop
git clone https://github.com/projectM-visualizer/presets-milkdrop-original
git clone https://github.com/projectM-visualizer/presets-milkdrop-texture-packpreset_root is defined in cleave-viz-default.yaml
python -m cleave play ~/music/mysong.wavThis will separate the track into its component stem tracks (bass, drums, vocals, other), perform some audio analysis, then launch the visualizer.
cleave creates a new directory under projects/ for each song, containing...
project.yaml- project metadatacleave-viz.yaml- visualizer configuration. Not everything in here is surfaced in the visualizer UI just yetsignals.json- audio analysis data used bycleave effectsmysong.wav- original source audio is copied into the project (makes a project self contained)stems/- separated audio stemsrenders/- final renders
python -m cleave --helpplayaccepts a source audio file or project slug/path.- It will only re-run the seperation and analysis if they're not already in the project directory
- Use
--forceif you want to redo these.
- Use
- It will only re-run the seperation and analysis if they're not already in the project directory
separatecan be run on its own without launching the visualizerrenderaccepts a project slug or path (not a source audio file).-ofor output (.mp4only)- If omitted outputs to
projects/<slug>/renders/<visualizer.name>.mp4
- If omitted outputs to
-cfor config-hq/--high-qualityforveryslowlibx264 encode (default uses ffmpeg's libx264 preset).
- Pass
-hq/--high-qualitytoseparateorplayfor higher-quality Demucs separation. - To store projects under XDG instead, set
CLEAVE_DATA=~/.local/share/cleave. python cleave.pyis an alias forpython -m cleave(same subcommands).
Optional title and body text burned into the MP4. Configure under render.overlay in cleave-viz-default.yaml (copied to each project's cleave-viz.yaml on first separate / play).
enabled— turn the overlay on or off.start_delay— when the overlay begins fading in (seconds).display_time— how long the overlay is on screen, including the 2s fade-in and fade-out.position—top-left,top-right,centre,bottom-left, orbottom-right.title/body— nested text blocks, each with:content— multiline string (|block scalar).font-size— text size in pixels (title is rendered bold).font-colour(title) orcolour(body) — hex text colour.background-colour— optional hex fill behind each line of text only (stops at the glyph width). Omit the key or leave empty for no text background.margin-bottom(title only) — gap in pixels between the title and body blocks.
background.margin— gap from the frame edge to the panel (ignored whenposition: centre).background.padding— gap from the panel edge to the text.background.colour,background.opacity,background.border— outer panel fill and border (border opacity matches background; border grows outward from the fill, margin is measured to the outer border edge).
In the live visualizer, a blank gap row separates the four stem layers from Render: OVERLAY. Same eye / expand / solo semantics as stem layers (solo forces the overlay on; solo is not saved). Tunable in the panel: position, opacity, border width, start delay, display time, and per-block font size and title margin-bottom under expandable title / body submenus. Content, colours, margin, and padding are YAML-only. Saved with SAVE AS NEW CONFIG / OVERWRITE CONFIG.
Whole-frame fade applied after the render overlay (GL fade on the composited image). Configure under render.post_fx in cleave-viz-default.yaml.
enabled— turn whole-frame fade on or off.fade_in— seconds to fade from black at the start of the video.fade_out— seconds to fade to black at the end.
Fade easing uses a smoothstep curve.
In the live visualizer, Render: POST FX sits below overlay with the same eye / expand / solo semantics (solo forces fade on; solo is not saved). Tunable in the panel: fade in, fade out. Saved with SAVE AS NEW CONFIG / OVERWRITE CONFIG.
Controls...
Up/Down- move up / down menu items
CTRL+Up/Down- move up / down layers
Right/Left- expand / collapse
- increment / decrement value by 1
- forward / back 10 secs
- next / previous milkdrop preset
CTRL+Right/Left- enable / disable layer
- increment / decrement value by 10
- forward / back 30 secs
- up / down the preset directory tree
SHIFT+Right/Left- Solo / unsolo layer
Enter- move a stem layer up or down the z-order (not available on Render: OVERLAY)
CTRL+Enter- lock / unlock stem layer
CTRL+q- quit
- The visualizer is four libprojectM layers at tiered resolutions, composited to 1280x720 @ 30 fps by default (editable
cleave-viz.yaml) - Milkdrop draws on black, so cleave treats black as transparent and uses pixel brightness as blend weight (
black-keydefault).
| Mode | Typical use |
|---|---|
black-key |
Background stems |
add |
Drums / highlights |
multiply, screen, others |
Experimental |
Signal-driven compositor modifiers on top of each layer. Tune depths (0-100%).
| Stem | Effects |
|---|---|
| Drums | pulse, flare, flash, grit |
| Bass | pulse (sub_bass, mid_bass), flash, grit |
| Vocals | pulse, hue (pitch), flash, grit |
| Other | pulse, flash, grit |