Universal Controller MIDI

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.

$ ucm --headless terminal daemon poll loop stdout NDJSON pipe / log
CLI invocation — headless daemon reads gamepad state and streams NDJSON events for scripting.

Full flag reference

Every flag the binary accepts. Use long form in scripts, short form for typing.

FlagShortArgumentPurpose
--headless-HNo GUI; runs as a daemon
--preset-pname or pathLoad preset by name or absolute path
--controller-cindexPick controller (0-based)
--port-namestringOverride virtual MIDI port name
--poll-hz250–1000Input poll rate override
--emit-json-JOne JSON event per line to stdout
--validatefile pathLint preset, exit non-zero on error
--print-configResolved config to stdout, exit 0
--portableRead/write config beside binary
--config-dirpathCustom config root
--log-leveltrace/debug/info/warn/errorDefault info
--log-filepathTee logs to a file
--diagnose-ble30 s BLE link quality report
--activate-licensekeyApply Pro license
--license-infoPrint activation status
--no-mdnsDisable mDNS / BLE-MIDI advertising
--osc-targethost:portAdd an OSC target without editing config
--version-vBuild + git hash
--help-hHelp 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.

OSPath
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

  1. 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.
  2. 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.
  3. CI for preset libraries. Run ucm --validate in a GitHub Action against every preset in a repo. Catch malformed JSON before it lands in the marketplace.
  4. Browser dashboard. --emit-json piped to websocketd drives a custom web UI that shows live event traces on a phone next to the rig. No app install needed.
  5. 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.

Edit this page on GitHub Updated
ESC

Type to search.