Loom has three pieces that talk to each other over a defined protocol.
[Bridge] class
Rust + GPU
runtime
Solid.js
The native plugin
The native plugin is a GPU-accelerated browser engine bundled inside the Unity package. It:
- Renders your UI to a surface Unity composites over the game scene.
- Forwards mouse and keyboard input from Unity into the UI.
- Carries state, actions, and events between C# and the UI runtime.
You don’t write code against the plugin directly. Your C# code uses the bridge.
The bridge
You write a C# class and tag it with [Bridge]:
[Bridge]
public partial class GameBridge {
[BridgeState] public int Score { get; set; }
[BridgeAction] public void Pause() { /* ... */ }
[BridgeEvent] public Event<DamageEvent> Damaged { get; } = new();
} A Roslyn source generator emits two things from this class:
- A runtime helper that serialises state and dispatches actions to the UI.
- A TypeScript
.d.tsfile that types the same surface for your UI code.
Your UI code uses useBridge() from @loomgui/bridge and gets fully typed bridge.state.score and bridge.actions.pause(), plus onEvent('damaged', …) for events.
See The bridge for the full data model.
The UI runtime
The UI is a normal Vite-built Solid app. @loomgui/vite-plugin handles the
bridge connection for you. useBridge() returns a live, reactive bridge
instance.
The same UI runs in two modes:
- Connected. The bridge talks to a live Unity Editor or player. State pushes from the engine; actions call back.
- Mock mode. No engine. Your
bootBridge({ ... })call provides mock actions and scenarios. The UI runs entirely in the browser. Used for fast iteration on visuals; see Mock mode.
Packaging
You install one Unity package. An Editor menu installs the npm packages into your UI app. See Sync UI Dependencies.