Driving a UI from DOTS means writing the same sync loop over and over: query the entity, read the component, copy the value into a label — every frame, for every stat. Add a health bar, an ammo counter, and a score, and you are maintaining a hand-rolled HUD system that breaks every time the data model shifts. Unity's own runtime binding can't help here: it reads managed objects, and your gameplay state lives in unmanaged ECS chunks it cannot see.
ECS Bridge is the binding layer DOTS never had. Mark a component, bind a VisualElement to a field, and it stays in sync — change-detected so unchanged values cost nothing, source-generated so the hot path stays Burst-friendly with zero reflection. The registry installs itself the first time you bind, ticks once per frame, and auto-disposes when an element leaves the panel.
One line per binding.
healthLabel.BindComponent<PlayerHealth>(entity, h => h.Current, BindTarget.Text, "HP: {0}");
healthBar.BindComponent<PlayerHealth>(entity, h => (float)h.Current / h.Max, BindTarget.StyleWidthPercent);
hudPanel.BindComponentToClass<PlayerHealth>(entity, h => h.Current * 5 < h.Max, "low-health");
Read any field through a strongly-typed lambda and write it to label text, a style value, a USS class, or visibility. Rename the field and the binding follows — no string paths, no runtime "field not found" surprises.
Singletons and global state.
Bind a label straight to an ECS singleton with BindSingleton<T> — ideal for score, wave number, or any game-state component, with no entity reference needed. For state that doesn't live on an entity at all — locale, settings, debug flags — ReactiveState<T> gives you an observable value with BindReactive and BindReactiveToClass, updated event-driven with zero per-frame cost while nothing changes.
Queries and aggregations.
Drive a label from a whole EntityQuery, not just one entity. BindCount, BindAny, and BindAggregate (Sum, Average, Min, Max) recompute as entities spawn and despawn. Bind a query straight to a ListView, ScrollView, or MultiColumnListView and the collection follows the world.
Events, thresholds, and async.
React to change instead of polling: OnComponentChanged, OnComponentThresholdCrossed, OnComponentIncreased / OnComponentDecreased, plus entity-lifecycle hooks OnEntityCreated and OnEntityDestroyed. Prefer to await? WhenComponentChangedAsync, WhenComponentThresholdCrossedAsync, and WhenEntityCreatedAsync return tasks with full cancellation support.
Two-way and commands.
UI can write back to ECS. BindTwoWayViaECB routes edits through an EntityCommandBuffer so writes coexist safely with running systems, and Button.BindCommand / BindCommandSingleton wire a button straight to a gameplay action.
Author HUDs in UXML.
Skip the per-label C# entirely — put the binding in the markup:
<engine:Label text="@ecs:PlayerHealth.Current,Max of #player as 'HP: {0} / {1}'" />
Then activate the whole document with a single root.BindEcsDocument(world, ("player", entity)) call. Malformed or unresolved markers warn once and skip — they never throw.
Change-detected, zero-allocation hot path.
Every binding supports three update strategies — Polling, Change Detection (the default), and Manual — switchable live with SetStrategy. Change detection suppresses writes for values that didn't move, so a thousand idle bindings cost almost nothing. Built-in Unity.Profiling markers and per-frame diagnostics (TicksThisFrame, WritesThisFrame, SkippedThisFrame) show exactly what the registry is doing.
See every binding live.
Window > KrookedLilly > ECS Bridge Inspector lists every active binding with its target, strategy, and entity, plus running tick and write counters and name/strategy filters — for leak hunting and for confirming a binding is actually firing.
Nine demo scenes included.
HealthBar — three bindings on one entity (label, bar fill, low-health USS class).
ScoreCounter — a singleton-driven score.
PlayerHUD — five concurrent bindings across an entity, a singleton, and a USS c
Description sourced from the Unity Asset Store listing.