Mock mode
Mock mode lets you run the UI in a normal browser, without Unity, without the
native plugin, without a WebSocket. Your bootBridge({ ... }) call provides
mock implementations of every action plus a set of named scenarios you can
trigger from a dev-tools panel.
Why mock?
- Speed. A Vite dev server reload is <100 ms. A Unity domain reload is 4–20 s. Iterating on layout, animation, and styling against the engine is painful; iterating in mock is instant.
- Determinism. Scenarios let you trigger specific UI states reproducibly: “Boot Progress 0.5”, “Take Damage”, “Open Pause Menu”. No need to play through the game to hit a particular screen.
- Designer-friendly. A non-Unity dev can run the UI app standalone and see the full flow.
Setting up mock mode
Your UI’s index.tsx calls bootBridge with two optional fields beyond the
initial state:
const bridge = bootBridge({ initial: { currentScreen: 'Splash', bootProgress: 0, counterValue: 0, },
// run when the UI calls bridge.actions.foo() in mock mode mockActions: { dismissSplash: () => { bridge.state.currentScreen = 'Main' }, increment: () => { bridge.state.counterValue += 1 }, // ... },
// trigger from the dev-tools panel mockScenarios: { 'Goto Splash': ({ state }) => { state.currentScreen = 'Splash' state.bootProgress = 0 }, 'Take Damage': ({ state, fire }) => { state.hudHealth = Math.max(0, state.hudHealth - 25) fire('damaged', { amount: 25 }) }, },})When the bridge boots without a loomWsUrl query parameter (i.e. you ran
npm run dev standalone), it enters mock mode. The actions provided in
mockActions run instead of being sent to the engine; the scenarios appear
in the floating dev-tools panel.
When connected
When bootBridge sees a loomWsUrl=... query string (set automatically
when running under Unity), it connects to that URL and ignores
mockActions. mockScenarios are also ignored in connected mode — the
engine is the source of state truth.
A tip
Keep the action surface in mock mode behaviorally identical to the engine side. The point is that the same Solid code runs against both. If your mock action does something wildly different from what the engine does, your mock-mode iteration loop is lying.