A native Tauri 2 + wgpu reference application that renders one authoritative GPU texture and routes it at runtime to multiple independent output sinks:
This is the composition step after the isolated recording, NDI, preview, Syphon, and Spout examples. It deliberately excludes the quarantined FFmpeg RTSP/RTMP streaming implementation.
Authoritative wgpu RGBA texture
├── Preview sink GPU presentation
├── NDI sink wgpu readback → BGRA → NDI worker
├── Platform-share sink
│ ├── macOS: Syphon wgpu readback → BGRA → Metal → Syphon
│ └── Windows: Spout wgpu readback → BGRA → D3D11 → Spout
└── Recording sink scaled texture → readback → FFmpeg
Every external output has its own lifecycle, bounded queue, status, and drop counters. A failing sink is intended to leave the authoritative renderer and other sinks running.
| Hotkey | Route |
|---|---|
1 |
Preview-only 1080p |
2 |
Syphon/Spout 1080p60 |
3 |
NDI 1080p60 |
4 |
H.264 recording 1080p30 |
5 |
NDI + Syphon/Spout + H.264 at 1080p30 |
6 |
4K30 ProRes + NDI + Syphon/Spout |
7 |
Hidden preview with NDI + Syphon/Spout at 1080p60 |
Arming a profile configures the route. It does not automatically start external outputs.
Install the full NDI SDK, not only NDI Tools. On macOS, the build searches standard SDK locations or uses:
export NDI_SDK_DIR="/Library/NDI SDK for Apple"
The universal Syphon.framework is bundled in src-tauri/vendor/. The project builds an Objective-C ARC bridge and embeds development and bundle-compatible runtime paths.
Install:
The project vendors the Spout2 source and builds a static Direct3D 11 bridge.
Full router:
npm install
npm run dev
Platform share without NDI:
npm run dev:no-ndi
Renderer, preview, and recording only:
npm run dev:renderer-only
npm run dev.5.npm run dev or npm run dev:no-ndi.2 first.Junkpile 39 in a Spout receiver or OBS Spout2 Capture.5 for simultaneous recording, NDI, and Spout.platformShare.adapterIndex in the JSON profile.The application seeds and watches a writable io-profiles.json in the operating system application-config directory. The active path is shown in the controls window.
Each profile declares:
{
"frame": { "width": 1920, "height": 1080 },
"preview": { "enabled": true },
"recording": {
"enabled": true,
"codec": "h264",
"fps": 30,
"workerDelayMs": 0
},
"ndi": {
"enabled": true,
"name": "Junkpile 39 NDI",
"groups": null,
"clockVideo": true,
"fpsN": 30,
"fpsD": 1,
"vflip": false
},
"platformShare": {
"enabled": true,
"name": "Junkpile 39",
"onlyWhenClients": true,
"adapterIndex": -1,
"fpsN": 30,
"fpsD": 1,
"vflip": false
}
}
onlyWhenClients applies to Syphon. adapterIndex applies to Spout. They remain in the common schema so the same profile file can move between operating systems.
The current NDI, Syphon, and Spout implementations each use an independent GPU readback path. Simultaneous routes therefore multiply memory bandwidth. The 1080p profiles are the primary stable target. The 4K profile intentionally exposes pressure on peak hardware.
The confirmed Syphon behavior is:
This is a staging-path limit, not a failure of Syphon discovery or publication.
FFmpeg RTSP/RTMP streaming is not included. Example 29 remains quarantined because UDP pacing, RTSP 400 responses, and RTMP broken-pipe failures were not resolved reliably. This router does not import that code.