| .github | ||
| Claude outputs | ||
| cmake | ||
| src | ||
| streamdeck | ||
| test | ||
| vendor/imgui | ||
| .clang-format | ||
| .gitignore | ||
| .gitmodules | ||
| CMakeLists.txt | ||
| dependencies_check.cmake | ||
| ImGui.cmake | ||
| install.cmake | ||
| LICENSE.md | ||
| packaging-macos.cmake | ||
| README.md | ||
| setup.sh | ||
projectM SDL Frontend — Control Window Edition
A fork of the projectM SDL frontend for macOS on Apple Silicon (arm64) only.
It listens to audio (including the system audio output on macOS 14.4+) and renders Milkdrop-style visuals with libprojectM. Compared to upstream, this fork:
- Moves every control into a separate control window. The visualizer window only ever shows the visuals, so it can go fullscreen on a projector or second display while you operate everything from the control window.
- Lets you configure everything from the UI. Playback, transitions, hard cuts, preset and texture folders, rendering, visualizer display/fullscreen/vsync, audio device, UI scale and theme. Changes apply immediately and are saved automatically — there is no "Save" button.
- Adds named playlists. Create, rename, duplicate and delete playlists; add presets or whole folders; reorder by
drag & drop; copy presets between playlists; filter by name. Playlists are stored as
.m3ufiles. - Makes autoplay a simple toggle. With autoplay on, presets switch after the display duration (in order or shuffled). With autoplay off, the current preset stays until you change it.
- Disables all keyboard shortcuts and mouse actions on the visualizer. Nothing happens by accident when a key is pressed or the mouse is clicked or scrolled over the visuals.
Using the application
When projectM starts, two windows open: the visualizer and the control window.
- Transport bar (top of the control window): current preset and playlist, Previous / Next / Random, Autoplay and Shuffle toggles, a countdown to the next preset, and buttons to show/hide the visualizer and to toggle fullscreen.
- Playlists tab: playlists on the left, presets of the selected playlist on the right.
- Double-click a preset to play it. This makes its playlist the active one.
- Cmd-click / Shift-click to select several presets. Drag them to reorder, or onto a playlist on the left to copy them there. Right-click for more actions (play next, move to top/bottom, copy to playlist, show in Finder...).
- More... offers sorting, shuffling the order, removing duplicates or missing files, and clearing the playlist.
- On first start, an All Presets playlist is created from the configured preset folder(s). It can be rebuilt at any time from More... or the Visuals tab.
- Playback, Visuals, Display, Audio, Interface tabs: all settings. Hover over a setting's name for a description. Reset restores the default value.
Drag & drop:
- Files or folders dropped onto the control window are added to the playlist shown in the Playlists tab.
- Presets dropped onto the visualizer are queued after the current preset (and played right away, if enabled). A folder dropped onto the visualizer creates a new playlist named after the folder (configurable).
Windows:
- Closing the visualizer window only hides it; show it again from the control window.
- Closing the control window (or using Quit from the macOS application menu) quits projectM.
- By default, fullscreen uses a desktop-sized window instead of a separate macOS Space, so the control window can stay visible on the same display. Enable Always on Top for the control window in the Interface tab to keep it above a fullscreen visualizer. Native macOS Spaces fullscreen can be enabled in the Display tab.
Files:
- Settings:
~/Library/Preferences/projectM/projectMSDL.properties - Playlists:
~/Library/Preferences/projectM/Playlists/*.m3u(one preset path per line; can be edited by hand while projectM is not running) - Log file:
~/Library/Preferences/projectM/projectMSDL.log
Quick start: one script does everything
git clone --recurse-submodules <this repository> frontend-sdl-cpp
cd frontend-sdl-cpp
./setup.sh
setup.sh checks your Mac, installs the Xcode command line tools and Homebrew packages if needed, builds
libprojectM (v4.1.7, as a static library), downloads the
Cream of the Crop preset pack (~9,800 presets) and
the Milkdrop texture pack, builds the app with
both bundled inside, signs it for your Mac and installs it to /Applications/projectM.app.
Everything it downloads and builds is kept in the .build folder, so running it again is quick. Run it again at any
time to update the presets and rebuild. Useful options (see ./setup.sh --help for all of them):
--classic-presetsalso bundles the original Milkdrop presets.--preset-dir DIR/--texture-dir DIRbundle your own presets or textures (can be repeated).--dest ~/Applicationsinstalls somewhere else;--no-installonly builds (the app ends up in.build/install).--cleanrebuilds everything from scratch.--no-streamdeckskips the Stream Deck plugin (it's only built when the Stream Deck app is installed).
If you already used projectM before, rebuild the "All Presets" playlist once to pick up newly bundled presets (Playlists tab > More... > Rebuild from Preset Folders).
Text on the visuals
The Text tab of the control window (or the Text: On/Off button next to Blackout) shows your own text in the center of the visuals, reacting to the music:
- 13 styles: Pulse, Neon, Glitch (RGB split, sliced lines, scrambled letters), Wave, Bounce, Shake, Rainbow, Strobe, Breathe, Outline, Cyberpunk (cyan/magenta ghosts, scanlines, glitches), Hologram (flicker, rolling scanlines) and Static.
- 25 bundled fonts (Tech, Retro, Neon, Display and Handwritten looks, all open-source licensed) or any
.ttf/.otf/.ttcfile. - Size in percent of the screen height (long text is shrunk to fit), color or inverse of the visuals, audio reactivity, opacity and fade time. Multi-line text works too.
- The text stays visible during a blackout, e.g. for a "Back in 5 minutes" message.
All of it is also available from projectmctl and the Stream Deck:
projectmctl text show style=neon font=tilt-neon color=#ff2a6d size=25 "Back in 5"
projectmctl text toggle style=cyberpunk font=orbitron color=invert "NIGHT CITY"
projectmctl text set size=30 # change options without showing / hiding
projectmctl text hide
projectmctl text styles # list the styles (text fonts: list the fonts)
Remote control: projectmctl
projectmctl controls a running projectM from the command line, e.g. from scripts, hotkey tools (Raycast, Alfred,
BetterTouchTool, Shortcuts...) or over SSH. setup.sh installs it next to the app and puts it on your PATH.
projectmctl next # next preset (alias: forward)
projectmctl prev # previous preset (alias: back)
projectmctl autoplay off # on | off | toggle
projectmctl playlist next # cycle through your own playlists (prev also works)
projectmctl blackout # fade to black (optionally: blackout 5 for a 5 second fade)
projectmctl resume # fade back in (optionally: resume 0 to cut back in instantly)
projectmctl status # current preset, playlist, autoplay and blackout state
projectmctl help # all commands
playlist next/playlist prevonly cycle through playlists you created: "All Presets" and empty playlists are skipped. The order is the same as in the control window.playlist listandplaylist play <name>are also available.- While the visuals are completely black, projectM doesn't render at all (saving power), so autoplay doesn't switch presets during a blackout. The control window has a matching Blackout / Resume button, and the default fade duration is set in the Playback tab.
- Exit codes:
0success,1the command failed (message on stderr),2projectM is not running. - It talks to projectM through a Unix socket that only your user can access:
~/Library/Application Support/projectM/control.sock(override with thePROJECTM_SOCKETenvironment variable). The protocol is one text line per connection, so you can also use it without projectmctl:echo "playlist next" | nc -U ~/Library/Application\ Support/projectM/control.sock.
Stream Deck plugin
The streamdeck folder contains a plugin for the Elgato Stream Deck app (version 7.1 or newer). setup.sh builds it
and installs it automatically when the Stream Deck app is installed; the actions then appear in the projectM
category. It talks to projectM through the same socket as projectmctl, so every projectmctl feature is available,
and the keys show the live state:
| Action | Key | Stream Deck + dial |
|---|---|---|
| Next / Previous / Random Preset | switch preset | – |
| Preset Dial | – | turn: previous / next preset; press: random (configurable); strip shows preset and time to next |
| Autoplay, Shuffle | toggle, or set to on / off; green while on | turn right: on, left: off; press: toggle |
| Next / Previous Playlist | cycle your playlists; shows the active one | – |
| Playlist Dial | – | turn: cycle playlists; press: toggle blackout (configurable) |
| Play Playlist | play the playlist chosen in the key's settings (drop-down filled from projectM); highlighted while playing | – |
| Blackout | toggle, fade out or fade in, with optional fade duration; red while black | turn left: fade out, right: fade in; press: toggle |
| Now Playing | playlist, position, preset name and a bar showing the time to the next preset | – |
| Text | show / hide your text with the key's style, font, size, color (or inverse) and reactivity, all set in the key's settings; previews the text and shows ON AIR while it's on screen; changes apply live | turn: text size; press / tap: show / hide |
Every key shows "OFFLINE" while projectM isn't running, and a warning triangle if a command fails (e.g. the chosen playlist doesn't exist).
To build it by hand: cd streamdeck && npm ci && npm run build && npm run pack, then double-click
dist/com.froz.projectm.streamDeckPlugin. During development, npx streamdeck link com.froz.projectm.sdPlugin and
npm run watch reload the plugin on every change. npm run icons regenerates the default images.
Building from source manually
Requirements: a Mac with Apple Silicon, macOS 12 or newer (system audio capture needs macOS 14.4+), the Xcode command line tools and Homebrew.
xcode-select --install
brew install cmake ninja sdl2 poco freetype git
1. Build and install libprojectM
git clone --recurse-submodules https://github.com/projectM-visualizer/projectm.git
cmake -G Ninja -S projectm -B projectm/cmake-build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_INSTALL_PREFIX=~/dev/projectm-install \
-DBUILD_SHARED_LIBS=ON
cmake --build projectm/cmake-build --parallel
cmake --install projectm/cmake-build
This fork only uses libprojectM's core library, not its playlist library.
2. Build this frontend
From this repository's directory (remember to initialize the Dear ImGui submodule):
git submodule update --init
cmake -G Ninja -S . -B cmake-build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="$HOME/dev/projectm-install;$(brew --prefix)" \
-DCMAKE_INSTALL_PREFIX=~/dev/projectm-app
cmake --build cmake-build --parallel
cmake --install cmake-build
projectM.app will be in ~/dev/projectm-app/, with projectmctl in projectM.app/Contents/MacOS/. To bundle presets and textures into the app, add
-DPRESET_DIRS=/path/to/presets -DTEXTURE_DIRS=/path/to/textures to the first cmake command. Otherwise, set your
preset and texture folders in the control window's Visuals tab and rebuild the "All Presets" playlist.
Curated presets: presets-cream-of-the-crop. Textures used by many presets: presets-milkdrop-texture-pack.
You can also run the binary straight from the build directory:
cmake-build/src/projectM.app/Contents/MacOS/projectM --presetPath /path/to/presets --texturePath /path/to/textures
Run it with --help to see all command-line options. Settings passed on the command line are marked with [!] in
the control window.
Platform support
The build refuses to configure on anything but macOS/arm64. -DPROJECTMSDL_ALLOW_UNSUPPORTED_PLATFORM=ON skips this
check for development compile checks only; such builds use a Dear ImGui file browser instead of the native macOS file
dialogs and are not supported.
License
GNU General Public License v3.0, like the upstream projectM SDL frontend. See LICENSE.md.