Advanced
CLI / power-user features
Headless mode, config file paths, JSON preset loading, log scraping. Run Universal Controller MIDI without the GUI — perfect for live rigs, dedicated boxes, and Raspberry Pi setups.
Updated
The CLI exposes everything the GUI does, minus the window. Run the bridge headless on a Raspberry Pi, a dedicated audio box, or as a launchd / systemd service. Same engine, same presets, no Electron overhead.
Invocation
The binary is named ucm. After install it's on your $PATH. The most common flags:
# run headless with a specific preset
ucm --headless --preset live-set-2026
# pick a controller by index (0-based)
ucm --controller 1 --preset bitwig-default
# pipe events to stdout in JSON for external tooling
ucm --headless --emit-json
# verify a preset file without launching
ucm --validate ~/presets/my-rig.json
# print the resolved config + exit
ucm --print-config ucm --help lists every flag with short descriptions. ucm --version prints the build hash. Both exit zero — safe to use in scripts.
Full flag reference
Every flag the binary accepts. Use long form in scripts, short form for typing.
| Flag | Short | Argument | Purpose |
|---|---|---|---|
--headless | -H | — | No GUI; runs as a daemon |
--preset | -p | name or path | Load preset by name or absolute path |
--controller | -c | index | Pick controller (0-based) |
--port-name | — | string | Override virtual MIDI port name |
--poll-hz | — | 250–1000 | Input poll rate override |
--emit-json | -J | — | One JSON event per line to stdout |
--validate | — | file path | Lint preset, exit non-zero on error |
--print-config | — | — | Resolved config to stdout, exit 0 |
--portable | — | — | Read/write config beside binary |
--config-dir | — | path | Custom config root |
--log-level | — | trace/debug/info/warn/error | Default info |
--log-file | — | path | Tee logs to a file |
--diagnose-ble | — | — | 30 s BLE link quality report |
--activate-license | — | key | Apply Pro license |
--license-info | — | — | Print activation status |
--no-mdns | — | — | Disable mDNS / BLE-MIDI advertising |
--osc-target | — | host:port | Add an OSC target without editing config |
--version | -v | — | Build + git hash |
--help | -h | — | Help text |
Config file paths
Per-user config lives in the OS-standard location. Edit by hand at your own risk — JSON is validated on load, malformed config falls back to defaults with a log warning.
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Universal Controller MIDI/ |
| Windows | %APPDATA%\Universal Controller MIDI\ |
| Linux | ~/.config/universal-controller-midi/ |
Inside each directory: config.json (global settings), presets/ (named preset bundles), logs/ (rotating logs, last 7 days), and license.json (Pro activation token — back it up).
Running as a service
On Linux, a systemd unit file boots the bridge at login:
[Unit]
Description=Universal Controller MIDI
After=sound.target bluetooth.target
[Service]
ExecStart=/usr/local/bin/ucm --headless --preset live --log-level info
Restart=on-failure
RestartSec=2
User=%i
[Install]
WantedBy=default.target On macOS, drop a plist in ~/Library/LaunchAgents/ and bootstrap it. Both keep the bridge alive across reboots.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.ucm.bridge</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ucm</string>
<string>--headless</string>
<string>--preset</string>
<string>live</string>
</array>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
</dict>
</plist> launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.ucm.bridge.plist loads it.
JSON emit format
--emit-json writes one event per line. Pipe to jq, into a custom script, or into websocketd to bridge to a browser. Combined with OSC mode this turns the bridge into a fan-out broker for any custom pipeline.
# tail events live, filter for trigger pulls only
ucm --headless --emit-json | jq -c 'select(.input | test("trigger"))'
# count notes per second
ucm --headless --emit-json | jq -c 'select(.type=="note_on")' | pv -l -i 1 > /dev/null
# bridge to a browser via websocketd (one-liner web dashboard)
websocketd --port=8080 ucm --headless --emit-json Real-world scenarios
- Raspberry Pi 5 live rig. Bridge on the Pi, GUI off, preset baked into
config.json. Boot to MIDI in 8 seconds. Touring without a laptop becomes plausible. - Studio always-on. Mac mini in a rack, launchd loads the bridge at boot, you forget it's there. Plug in any controller, it just shows up in the DAW.
- CI for preset libraries. Run
ucm --validatein a GitHub Action against every preset in a repo. Catch malformed JSON before it lands in the marketplace. - Browser dashboard.
--emit-jsonpiped towebsocketddrives a custom web UI that shows live event traces on a phone next to the rig. No app install needed. - Game streaming overlay. JSON events feed an OBS browser source — viewer sees controller state during creative-coding streams. Pair with the keyboard-shortcuts page for hotkey overlays.
CLI usage is unlocked in both Free and Pro builds — the same feature gates that apply in the GUI apply at the CLI.