Getting started
First launch + onboarding
What happens the first time you open Universal Controller MIDI. Permissions, the onboarding wizard, default mapping, and the first MIDI port.
Updated
The first launch of Universal Controller MIDI runs a 90-second, four-step wizard — permissions, controller pick, virtual port, starter template. End state: a paired controller, a virtual MIDI port live, and audio already playable in your DAW before you've touched the mapping editor.
Step 1 — Permissions
The wizard surfaces only the OS prompts you actually need. On macOS that's Input Monitoring and (for wireless) Bluetooth. On Windows, nothing — HID is open by default. On Linux it confirms the udev rule is loaded.
If a permission was denied, the wizard shows a deep link directly to the right OS pane instead of making you hunt.
The four onboarding steps at a glance
Here's what runs, in what order, and roughly how long each step blocks for. Total: ~90 s.
| Step | Action | Typical duration | Skippable? |
|---|---|---|---|
| 1 | OS permission grants | 5–20 s | No — controller can't read otherwise |
| 2 | Pick a controller | 10–40 s | Yes — defer with "I'll connect later" |
| 3 | Create virtual MIDI port | 2–5 s | No — required for DAW output |
| 4 | Choose starter template | 5–15 s | Yes — pick "Empty" to skip |
Step 2 — Pick a controller
Plug in a wired controller or pair one over Bluetooth. The wizard scans every ~200 ms and lights up matching devices the moment they appear. Supported families: DualSense, DualShock 4, Xbox One/Series, Switch Pro, Stadia, 8BitDo, generic XInput, generic HID gamepads.
Step 3 — Create a virtual MIDI port
The wizard prompts you to name the virtual port. Default name: Universal Controller. This is the name your DAW will see in its MIDI input list.
On macOS you'll be sent to the IAC setup page. On Windows the port is created automatically via the built-in teVirtualMIDI driver bundled with the app. On Linux it's an ALSA seq client — no extra step.
Step 4 — Pick a starter template
The wizard offers four starters: Drum Pads, Synth XY, DJ Decks, and Empty. Drum Pads is the default — face buttons map to a GM kick/snare/hat layout, triggers map to velocity. Pick one and you're done.
You can swap templates any time from the templates panel.
Where the onboarding state is stored
The wizard writes a single JSON blob to the app's config directory the moment it finishes. Re-running it is as simple as deleting that file. Locations:
# macOS
~/Library/Application Support/UniversalControllerMIDI/onboarding.json
# Windows
%APPDATA%\UniversalControllerMIDI\onboarding.json
# Linux
$XDG_CONFIG_HOME/universal-controller-midi/onboarding.json
# (defaults to ~/.config/universal-controller-midi/) The file shape is tiny — useful to know if you're scripting first-run setup across multiple machines:
{
"completedAt": "2026-05-29T09:14:22Z",
"version": 4,
"permissions": { "inputMonitoring": true, "bluetooth": true },
"controller": { "vendor": "054c", "product": "0ce6", "name": "DualSense" },
"midiPort": { "name": "Universal Controller", "channel": 1 },
"template": "drum-pads"
} What if it doesn't work?
If onboarding stalls or restarts, one of these is almost always the cause:
- Wizard re-opens every launch — the config dir isn't writable. Check perms on the path listed above. On Linux it's commonly
~/.configbeing owned by root after ansudolaunch. - Controller never appears in step 2 — wired? Try a different USB port. Wireless? Walk through connect Bluetooth first, then resume the wizard.
- Virtual port creation fails on Windows — the
teVirtualMIDIdriver didn't install. Re-run the installer elevated and reboot. See virtual-port-windows. - Template list is empty — the template bundle didn't ship with the build. Re-download from store.aidxn.com — the SHA on the changelog page is the source of truth.
- "Permission denied" toast on macOS step 1 — TCC has stale entries from an old build with the same bundle id. Run
tccutil reset All com.aidxn.universalcontrollermidiin a terminal, then re-launch.
What's next
Wire your DAW to the new virtual port, then jump to verify everything works for a loopback test.