■ huff Home How It Works Install Interface MIDI OSC Output
--:--:--
Architecture
_

Project Structure

huff/ ├── src/ │ ├── index.html # Controls window — UI, p5 draw loop, effect chain │ ├── canvas.html # Output mirror window │ ├── canvas.js # p5 setup, draw loop, video handling, WS sender │ ├── effects.js # applyGlitch · applyTrails · applyScanlines │ │ # applyFlowWarp · applySolarize · applySymmetry │ ├── p5.js # p5.js library (bundled locally) │ ├── midi/ # Bundled MIDI map files │ ├── osc/ # Bundled OSC map files │ └── presets/ # Factory preset JSON files │ ├── src-tauri/ │ ├── src/ │ │ ├── main.rs # WS relay · MIDI commands · OSC listener │ │ │ # Syphon handlers · Spout handlers │ │ ├── syphon.rs # macOS Syphon via ObjC FFI (Metal texture) │ │ └── spout.rs # Windows Spout2 via SpoutDX C-ABI bridge │ ├── native/ │ │ └── spout_bridge/ # C++ SpoutDX bridge (CMake) │ ├── frameworks/ │ │ └── Syphon.framework │ └── tauri.conf.json │ └── scripts/ ├── create_universal_dmg.sh └── tauri-build.cjs

WebSocket Relay

The relay runs inside the Tauri Rust process — not in Node.js or the browser.

  • Binds on 127.0.0.1:8787 (IPv4) and [::1]:8787 (IPv6) via tokio + tokio-tungstenite
  • Clients register via a JSON hello: {"type":"hello","role":"index"} or "canvas"
  • Text messages → broadcast to all other clients
  • Binary messages → inspected for magic prefix: HUFFSYPH → Syphon, HUFFSPOUT → Spout, all other → canvas clients

Rust Crates

CrateVersionPurpose
tauri1.xApp shell, two-window management, Tauri IPC
tokio1.xAsync runtime (relay + OSC listener)
tokio-tungstenite0.21WebSocket server
midir0.9Native MIDI input (USB + virtual)
rosc0.10OSC UDP packet decoder
once_cell1.xLazy static initialisation for global client map
serde / serde_json1.xJSON IPC serialisation
objc / objc-foundation0.2 / 0.1macOS Syphon ObjC FFI (macOS only)
cmake0.1Spout bridge CMake build (Windows only)

Frontend Architecture

The frontend intentionally has no build step and no bundler. Everything is plain ES6 loaded via script tags. This keeps the dev cycle fast — edit a file, reload the WebView, see the change immediately.

index.html is large (~2300 lines) because the entire controls UI, all MIDI/OSC/Syphon/Spout logic, and the p5 draw loop all live in one file. This is intentional — a single file is trivially auditable and has no dependency graph to maintain.

effects.js is the one separate module because its functions are called from both canvas.js and index.html. The only shared interface between the two is the frameRing array and gBuf p5 Graphics object.


macOS Universal Binary Build

scripts/create_universal_dmg.sh ↓ tauri build --target aarch64-apple-darwin → huff_arm64.app tauri build --target x86_64-apple-darwin → huff_x64.app ↓ lipo -create huff_arm64.app/.../huff huff_x64.app/.../huff -output huff_universal.app/.../huff ↓ hdiutil create → huff-universal.dmg