| .github/workflows | ||
| frontend | ||
| src | ||
| tests | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| bun.lock | ||
| config.example.json | ||
| electrobun.config.ts | ||
| LICENSE | ||
| MAPPING.md | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
TheatreSonus
This bridge lets you control a PreSonus StudioLive III console from TheatreMix by spoofing the X32/M32 OSC protocol that TheatreMix expects and translating those commands to the native PreSonus network API.
- Preamble
- What it does
- Supported mappings
- Requirements
- Installation
- Configuration
- Running
- Using with TheatreMix
- Architecture
- Known limitations
- Testing
- Troubleshooting
- License
Preamble
Full disclosure: this is slop. I needed a quick and dirty way of using TheatreMix with the PreSonus board at my job because they're too cheap to rent an X32 and I can't afford to leave mine set up for the full run of the show we're about to do. I absolutely love TheatreMix and I implore you to go support them in whatever way you can. I've tried my best to avoid interfering with their licensing and as such I've included a "more proper" legal disclaimer toward the end of this document, but here's the long and short:
- I vibecoded this in a couple hours based on the publicly available X32 OSC docs and
@featherbear/presonus-studiolive-api. - It probably doesn't work for all cases, I just needed basic West End automation.
- I might work on this to make it more robust, but I probably won't unless it's completely necessary.
- This is not intended to step on TheatreMix's toes. They've made some great software but it just doesn't support the board I need it to work on for this particular gig, and I don't expect them to really bother supporting PreSonus because their boards are shitty, so I had to devise something.
- I'm not a fan of vibecoding on ethical grounds but I needed to quickly generate a translation layer on short notice and that's kinda exactly what it does best 🤷♂️
All that said, I'm more than open to any PRs, discussions, what-have-you, but this project isn't a huge priority for me. Feel free to fork if you wanna build it out but PLEASE respect the original TheatreMix license terms.
What it does
- Listens on UDP port
10023and behaves like a Behringer X32/M32 console. - Connects to a StudioLive III mixer over TCP port
53000using@featherbear/presonus-studiolive-api. - Translates X32 OSC commands to PreSonus API calls and vice-versa.
- Supports channel faders, mutes, solos, names, colours, bus/FX sends, DCA faders, and DCA assignments.
- Forwards meter data so TheatreMix can display level meters.
- Allows TheatreMix scene cues to recall PreSonus project scenes.
Supported mappings
| X32 concept | PreSonus concept |
|---|---|
/ch/01-32 |
LINE 1-32 |
/auxin/01-08 |
RETURN 1-8 |
/fxrtn/01-08 |
FXRETURN 1-8 |
/bus/01-16 |
AUX 1-16 |
/main/st |
MAIN |
/dca/1-8 |
DCA (filtergroup) 1-8 |
/ch/XX/grp/dca |
Per-DCA membership (filtergroup) |
/-stat/solosw/XX |
Per-channel solo state |
/load scene N |
Recall PreSonus project scene N |
For the full mapping table, see MAPPING.md.
Requirements
- Bun 1.0+
- A PreSonus StudioLive III mixer reachable on the network
- TheatreMix pointed at the computer running this bridge
Installation
This project uses Bun. Make sure Bun is installed, then install the dependencies and verify the type check:
bun install
bun run typecheck
The @featherbear/presonus-studiolive-api, bunyan, osc-min, and electrobun dependencies are resolved from the npm registry.
Configuration
Configuration is read from a JSON file. Copy the example file and edit it for your network:
cp config.example.json config.json
config.json:
{
"presonusHost": "192.168.1.42",
"presonusPort": 53000,
"x32Port": 10023,
"consoleName": "X32",
"consoleModel": "X32",
"consoleVersion": "4.13",
"serverVersion": "V2.07",
"pollIntervalMs": 1000,
"xremoteRenewalMs": 9000,
"logLevel": "info"
}
| Field | Default | Description |
|---|---|---|
presonusHost |
192.168.0.10 |
IP address of the StudioLive console |
presonusPort |
53000 |
PreSonus TCP port |
x32Port |
10023 |
UDP port to listen on for TheatreMix/X32 OSC traffic |
consoleName |
X32-Desk |
Name advertised to TheatreMix |
consoleModel |
X32 |
Model advertised in /info//xinfo (X32 or M32) |
consoleVersion |
4.13 |
Firmware version advertised in /info//xinfo |
serverVersion |
V2.07 |
OSC server version advertised in /info |
advertisedHost |
(auto) | IP advertised in /xinfo; auto-detected if blank |
discoveryBroadcastIntervalMs |
5000 |
How often to broadcast /xinfo discovery packets (ms); 0 disables |
pollIntervalMs |
1000 |
How often to sync names/colours/DCAs (milliseconds) |
xremoteRenewalMs |
9000 |
/xremote subscription lifetime (milliseconds) |
logLevel |
info |
Bunyan log level |
logWebSocketPort |
9000 |
Port for the WebSocket log broadcaster; omit to disable |
channelOffset |
0 |
Offset added to X32 /ch/X numbers when addressing PreSonus LINE channels |
The bridge will not start if presonusHost is missing or blank. Set it to your console's IP address.
You can also override the config file path:
# via environment variable
CONFIG_PATH=/path/to/my-config.json bun start
# or as the first CLI argument
bun start /path/to/my-config.json
Running
bun start
Or build and launch the desktop app in development mode:
bun run dev
Using with TheatreMix
- Start the bridge on a computer on the same network as your StudioLive console.
- In TheatreMix, add a new X32/M32 console and enter the bridge computer's IP address (not the console's IP).
- TheatreMix will discover the bridge as an X32 and send OSC commands; the bridge forwards them to the StudioLive.
Log viewer (Astro frontend)
A small Astro frontend in frontend/ displays live bridge logs in an 800x600 window and provides an in-app config editor. It connects to the backend over WebSocket (ws://127.0.0.1:9000/logs by default).
Build the frontend:
cd frontend
npm install
npm run build
The static site is output to frontend/dist/.
Config editor
Click the Config button in the header to open an inline editor for all bridge settings (presonusHost, ports, log level, channel offset, etc.). Changes are saved via POST /config to the backend and written to config.json — restart the bridge to apply them.
Launching with Electrobun
The project uses Electrobun to package the bridge and the Astro log viewer as a single desktop app. The main process entrypoint is src/bun/index.ts, and the built Astro frontend is bundled as views://mainview/index.html.
Build the frontend and the desktop app:
bun run build:frontend
bun run desktop
Or run in development mode (build + launch + watch):
bun run dev
The dev build produces an app bundle at:
./build/dev-macos-arm64/TheatreSonus-dev.app
Open it like any other macOS app:
open "./build/dev-macos-arm64/TheatreSonus-dev.app"
On first launch, if config.json is not found, the app copies the bundled config.example.json next to the launcher as config.json so you can edit it. Edit presonusHost (and any other settings) there, then relaunch the app.
The WebSocket URL can be overridden with a query parameter:
views://mainview/index.html?ws=ws://127.0.0.1:9000/logs
Set logWebSocketPort in config.json to change the backend port or omit it to disable the broadcaster.
Channel offset
If your StudioLive has more inputs than the X32 surface exposes, you can offset which PreSonus LINE channels are controlled by X32 /ch/X paths. Set channelOffset to the number to add:
{
"channelOffset": 32
}
With the above, TheatreMix /ch/01 through /ch/16 control PreSonus LINE 33 through LINE 48. The offset only applies to input channels (/ch/X ↔ LINE); buses, auxes, FX returns, DCAs, mains, and solos are unaffected.
Architecture
TheatreMix --OSC/UDP--> X32OscServer --TheatreSonusBridge--> PresonusClient --TCP--> StudioLive
Frontend --WS/logs----> LogBroadcaster
--HTTP/config-> (port 9000)
src/x32osc.ts— UDP OSC server that mimics an X32/M32.src/presonus.ts— Thin wrapper around@featherbear/presonus-studiolive-apiwith helpers for names and DCA membership.src/mapping.ts— Bidirectional translation between X32 addresses and PreSonus selectors/state paths.src/meters.ts— Converts PreSonus meter UDP packets into X32-style/meters/Xblobs.src/bridge.ts— Orchestrates both sides, handles subscriptions, and broadcasts state changes.
For a deeper dive, see ARCHITECTURE.md.
Known limitations
- Some X32 features have no StudioLive equivalent (e.g., matrix buses, X32 mute groups, X32 FX parameters).
- Metering is approximated from the available PreSonus meter groups.
- The bridge targets TheatreMix's core workflow: faders, mutes, solos, DCAs, names, and scene recall.
Testing
bun test
Run type checking only:
bun run typecheck
Troubleshooting
TheatreMix cannot connect
- Confirm the bridge is running and listening on the expected UDP port (
x32Port, default10023). - In TheatreMix, use the bridge computer's IP address, not the StudioLive console's IP address.
- Check that there is no firewall blocking UDP port
10023on the bridge computer.
State is not syncing
- Verify
presonusHostandpresonusPortinconfig.jsonare correct. - Check the logs for connection errors or timeouts.
- Ensure the console is powered on, has a project loaded, and is on the same network as the bridge.
Missing meters or DCAs
- Meter data depends on the console sending UDP meter packets; some StudioLive firmware versions require UC Surface to be connected first.
- DCA assignments are polled every
pollIntervalMs; large values will delay updates.
"Presonus console not connected; dropping oldest queued command"
This warning means the bridge received X32 commands while the TCP connection to the StudioLive console was not ready. The bridge keeps a small queue and drops the oldest command once it fills up.
- Verify
presonusHostandpresonusPortinconfig.json. - Make sure the console is powered on and on the same network as the bridge.
- Check that nothing else (e.g., UC Surface) has exclusive control of the console.
- The warning is rate-limited; if it repeats, enable debug logging to see connection state changes.
Enable debug logging
Set logLevel to debug in config.json to see every incoming OSC message and discovery response. The configured log level controls both the console output and the WebSocket log stream to the frontend.
You can also override the log level for a single run with the LOG_LEVEL environment variable:
LOG_LEVEL=debug bun start
To disable pretty logs and output raw JSON (useful for log aggregation), set PRETTY_LOGS=false:
PRETTY_LOGS=false bun start
Disclaimer
This tool is a standalone network proxy. It passively listens for standard X32 OSC datagrams broadcast over the local network. It does not load, execute, patch, read the memory of, or disassemble the TheatreMix application. All control translations are based on Behringer's publicly published X32 OSC specification. TheatreMix is a registered trademark of Mixing Technology Pty Ltd; this project is not affiliated with or endorsed by them.
Parts of this codebase were written with assistance from generative AI tools. While the bridge includes input validation and clamps values such as fader levels to safe ranges, it is not guaranteed to be completely free of errors or to handle every edge-case OSC packet that TheatreMix or another X32 client may emit. StudioLive consoles can execute real-time audio changes (including mutes, level changes, and scene recalls). Use this bridge at your own discretion and only in situations where an unexpected command would not cause injury, equipment damage, or an uncontrolled live-audio event.
By using this software you agree that the authors and contributors assume no liability for any loss, damage, or disruption caused by commands sent to your console, including those that may result from misinterpreted OSC values sent by third-party applications such as TheatreMix. Refer to TheatreMix's own End User License Agreement (e.g., Clause 13.2 and 13.3 regarding errors and liability) for the terms governing your use of their application.
License
GNU General Public License v3.0