Skip to content

How it works

Loom is three pieces that talk to each other through a defined protocol.

flowchart LR
    subgraph Game ["Unity Editor / Player"]
        CS["C# code<br/>[Bridge] class"]
        Native["Native plugin<br/>(Rust + GPU)"]
        CS <--"FFI"--> Native
    end

    subgraph UI ["UI process"]
        JS["JS components<br/>(Solid.js)"]
        Boot["@loomgui/bridge<br/>runtime"]
        JS <--"reactive"--> Boot
    end

    Native <--"WebSocket<br/>MessagePack"--> Boot

    classDef cs fill:#1b1f3a,stroke:#7c87ff,color:#f4f4f8
    classDef js fill:#11332b,stroke:#5cdbb5,color:#f4f4f8
    class CS,Native cs
    class JS,Boot js

The native plugin

The native plugin is a Rust-based GPU engine bundled inside the Unity package. It:

  • Renders your UI to a surface Unity composites over the game scene.
  • Forwards mouse, keyboard, and touch events into C# input handling.
  • 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 Observable<int> Score { get; } = new(0);
[BridgeAction] public void Pause() { /* ... */ }
[BridgeEvent] public Event<DamageEvent> Damaged { get; } = new();
}

A Roslyn source generator emits two things from this class:

  1. A runtime helper that serialises state and dispatches actions to the UI.
  2. A TypeScript .d.ts file that types the same surface for your UI code.

Your UI code uses useBridge() from @loomgui/bridge and gets a fully typed bridge.state.score, bridge.actions.pause(), bridge.events.damaged.on(...).

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.

The packaging story

You install one Unity package. An Editor menu installs the npm packages into your UI app for you. See Sync UI Dependencies.