The bridge
A Loom bridge is a C# class tagged with [Bridge]. It defines the
contract between your game code and your UI. Three things cross the wire:
| Direction | Mechanism | When |
|---|---|---|
| C# → UI | [BridgeState] Observable properties | Whenever .Value changes |
| UI → C# | [BridgeAction] methods | Whenever the UI calls bridge.actions.foo(...) |
| C# → UI | [BridgeEvent] Event properties | Whenever C# calls .Fire(payload) |
Each member of your bridge class falls into one of these three buckets.
State
State is reactive. You declare it once with an initial value; the UI sees a typed Solid signal-like accessor.
[BridgeState]public Observable<float> Health { get; } = new(100f);
// later, on damage:Health.Value = Mathf.Max(0f, Health.Value - 10f);const bridge = useBridge()// reactive — re-renders when Health changesbridge.state.health // numberThe source generator names match by convention: C# Health becomes TS
health. Acronyms (e.g. HUD, UI) follow C# casing — HudVisible
becomes hudVisible.
Container types
Beyond scalar observables, Loom provides:
- ObservableList — reactive list. UI sees array-like access with fine-grained reactivity (single-item changes don’t re-render the whole list).
- Nested DTO classes. Properties of a
[Bridge]class can be POCOs with their own Observable properties. The generated TS types nest the same way.
See the Observable and Event reference for the exact runtime semantics.
Actions
Actions are methods. The UI calls them; C# executes them on the main thread during the next pump.
[BridgeAction]public void TakeDamage(float amount) { Health.Value = Mathf.Max(0f, Health.Value - amount);}bridge.actions.takeDamage(10)Actions are fire-and-forget; they don’t return values to the UI. If your UI needs the result of an action, the action should mutate state — which the UI sees reactively.
Events
Events are one-shot fan-outs. C# fires them; the UI listens.
[BridgeEvent]public Event<DamageEvent> Damaged { get; } = new();
// later:Damaged.Fire(new DamageEvent { Amount = 10, IsCritical = true });import { onEvent } from '@loomgui/bridge'
onEvent('damaged', (e) => { // e: { amount: number; isCritical: boolean } flashRed()})Events carry typed payloads. The DTO class (DamageEvent above) is a plain
C# class with [Serializable]-style fields — the source generator picks up
its shape and types the TS callback parameter.
See the DTO conventions reference for the rules.
Example: the sample HUD
The in-repo Unity sample’s ↗ unity/sample/Assets/Scripts/SampleBridge.cs
puts all three together in ~30 lines. Its ↗ unity/sample/UI/src/screens/Hud.tsx shows the matching Solid component.