Consystence
Start free
Back to blog
Updated 15 July 2026

One semantic UI tree, many renderers

Our control screens are not an SPA. The server drives every screen, and holds one semantic UI tree the CLI already renders live — with the operator faceplates and a native renderer following. Here is how that works and why we chose it.

Garry Thomas
ArchitectureUIEngineering

Update — 15 July 2026. The bet below has since paid off in the browser too: the console has been ported onto the same semantic wire tree the CLI renders — overview, process, alarms and events, hardware and controllers, trends, admin, and fleet views now all resolve through the one tree, with the CLI as the parity oracle. The native renderer remains roadmap.

Ask three industrial dashboards for the state of the same pump and you can get three answers. One is built as a web app, one as whatever the operators actually use at the panel, one for the phone someone wanted it on — and a renamed tag or a changed alarm threshold is enough to set them disagreeing. We did not want to maintain three truths, so we built one.

On Consystence, a control screen is a single semantic UI tree that lives on the server. It describes what the operator should see — a faceplate for PP-01, its running state, its flow and head readouts, the alarms in scope — not how any particular device should paint it. Renderers attach to that tree and draw it. Today the CLI renders the first surface already migrated to that tree; the browser renders the control screens over the established server-driven pipeline, with the operator faceplates migrating onto the same tree next. A native renderer is on the roadmap. The screen is authored once.

Server-driven, streamed over SignalR

The browser does not hold the application logic. It holds a renderer. The server assembles the UI tree from the live plant state and the operator’s current query — show me the transfer pumps in alarm — and streams the result down over SignalR. When state changes, the server sends a patch, not a page; the renderer applies it. There is no SPA framework to maintain, no client-side store to keep in sync with the PLC, and no second copy of the alarm logic shipped to the browser where it can rot.

This matters more than it sounds. In a control room the question is always “what is true right now?”, and the only authoritative answer lives next to the data — at the site server, beside the historian and the device model. Putting the UI tree there means the thing deciding what to show is the same thing that knows what is happening. The renderer is deliberately dumb so that it can never disagree with the plant.

The same tree paints the CLI

Because the tree is semantic rather than pixel-shaped, it does not assume a browser. Our CLI client subscribes to the same stream and renders the same tree as text — in a terminal at the edge, over an SSH session, or anywhere a window manager is a luxury. This is not a cut-down “API mode”; it is the identical UI tree, rendered differently. The surfaces already on the tree render live in the terminal today; as the operator faceplates follow, a commissioning engineer standing at a PLCnext controller with nothing but a laptop and a console gets the same screen as the browser — the difference between having the HMI and not.

It also keeps us honest. If a screen only makes sense as a dense pixel layout, it was probably encoding presentation where it should have encoded meaning. Forcing every screen to render legibly in a terminal pushes the design toward state and intent and away from decoration.

What ships, and what is next

To be precise about where the line is:

  • Shipped: the server-driven UI over SignalR, and the semantic-tree contract proven end to end by the CLI renderer against the first migrated surface — the admin/API-keys management screen, which the terminal subscribes to and draws live (open, patch, and invoke) exactly as the browser would.
  • Roadmap: a WinUI3 native renderer for the Windows panels that still dominate plant rooms, attaching to the very same stream.

The native renderer is genuinely future work, and we will not pretend otherwise. But the important architectural bet is already paid off: because the contract is one tree, many renderers, adding the native client is a rendering problem, not a rebuild. The alarm logic, the device types, the faceplate definitions, and the live state all stay exactly where they are — on the server, authored once, true everywhere.

That is the whole idea. Build the screen once, against meaning rather than markup, and let each surface draw it. The browser, the terminal, and eventually the native panel are all looking at the same plant — because they are all looking at the same tree.