Skip to content
Pre-release · Unreal Engine 5.6 / 5.8Source plugin · verified on Linux

VaCuus

Your game's interface in HTML and CSS, rendered entirely off the game thread.

0.012 msgame thread per frame at the 1,732-node reference worst case — budget 0.10 ms

You author screens as .rml / .rcss documents — the web languages, not a UMG graph — and the plugin runs them on its own UI thread: layout, styling and draw-command recording all happen away from the game thread, which only enqueues input and reads back a snapshot. Full-screen HUD, menu, or a panel on a quad in the world.

A dense combat HUD over a desert scene: two 24-row scoreboards, a
                   killfeed, floating damage numbers, a compass, a minimap, an ammo counter
                   and a settings panel.
vacuus.RefHudthe 1,732-node reference HUD the plugin ships, rendered headless by the plugin itself at 1920×1080
01

Off the game thread, measured

docs/perf-guide

One UI thread owns every document: layout, styling and draw-command recording. The render thread replays the resulting command buffer into a persistent render target, and Slate composites that target every engine frame. What is left on the game thread is enqueueing input and reading one published snapshot.

Publication is withheld when a frame's content hash is unchanged and no resource traffic occurred, so a static UI stops costing anything to draw: the static M1 HUD published 1 frame in 6,910, and the glass demo 0 of 3,363. The reference HUD below publishes 100% of frames by design — it animates every one of them, which is why it is the worst case.

Every figure here is measured and re-runnable in your own project: vacuus.RefHud to load the scene, vacuus.M1HUD.PerfLog 1 to print the timings and the publish ratio.

Reference worst case — 1,732 live nodes, 908 draws/frame, 1080p
Game thread / frame budget 0.10 ms · ~8–11×0.012 ms dev
0.008–0.009 ms shipping
Composite only, idle UI budget 0.05 ms · ~12–25×0.002 ms shipping
0.003 ms dev
UI thread, update + record budget 1.2 ms steady avg1.050 ms shipping
1.077 ms dev
Added RAM budget 32 MB · A/B median+14.3 MB
Added disk, shipping budget 10 MB · Linux proxy+3.22 MiB
02

Plain text, live reload

docs/setup

.rml is the markup, .rcss the styles, and both are plain files on disk. There is no editor asset to open and no graph to wire — your edit is complete when the file is saved, and it diffs, reviews and merges like the rest of your source.

Screens live under Content/DevUI: the plugin's own tree ships the demos, and your project's tree is an extension point beside it, not an override.

  1. Save the file. Any editor. No import step, no reimport prompt, no asset registry entry.
  2. The watcher reloads the document in place while PIE keeps running — the editor watches Content/DevUI.
  3. At runtime, vacuus.ReloadUI does the same thing from the console.
  4. No recompile. C++ is what hosts the document; it is not what the document is made of.
03

Built for AI agents

docs/ai-guide

The entire UI surface is text an agent can read and write. That is the easy half. The hard half is that most authoring mistakes here produce no error and a screen that renders — just not the one you asked for. A human notices immediately; an agent, which cannot see the screen, does not.

So the package ships the other half too: a front door the agent finds on its own, a generated ground truth to check itself against, and a verification loop that works without eyes.

One line in your project's CLAUDE.md / AGENTS.md
UI is VaCuus (HTML/CSS off the game thread). Before touching anything under
Content/DevUI, read Plugins/VaCuus/docs/buyer/ai-guide.md and
Plugins/VaCuus/docs/buyer/rcss-matrix.md.

AGENTS.md

At the package root, where an agent looks first. Short by design: the five things that would otherwise cost the session, and links to the real pages.

ai-guide.md

The full brief. Which mistakes warn, which are drawn wrong in silence, and how to tell the two apart.

rcss-matrix.md

99 properties, 20 shorthands, 16 decorators — parsed out of the exact RmlUi vendored in the package, not out of a model's training data. If a property is not in that file, it does not exist.

The verification loop

A declaration RmlUi cannot parse logs a warning naming file:line, in every configuration including Shipping. Plus a headless 1920×1080 render recipe and the 227-test suite, so an agent can check its own work.

04

Engine-native data

docs/setup

Data models bind straight from your UPROPERTY fields. The plugin reads them through the engine's own reflection — no mirror struct to keep in sync, no serialisation step between your game state and the document.

JavaScript is optional, not assumed. QuickJS-ng is vendored in-tree for the projects that want scripting, and a TypeScript/Preact workflow sits on top of it when you want that too.

  1. UVaCuusWidget — "VaCuus View" in the UMG palette. Screen space: HUD, menu, overlay. Drop it into any UMG tree and point it at a document.
  2. UVaCuusWorldComponent — "VaCuus World Panel". A pixel panel on a quad in the world.
  3. Both work from Blueprint and from C++. Either one hands you a view, and the view is what you feed and drive.
05

Rendered by the plugin

headless, 1920×1080
227automation tests, shipped with the pluginAutomation RunTests VaCuus
99 / 20 / 16RCSS properties, shorthands, decoratorsgenerated from the vendored RmlUi
0.012 msgame thread per frame at 1,732 nodesbudget 0.10 ms · dev build
5.6 · 5.8engines, built and tested from one tree5.7 untested · verified on Linux
06

What it is not

docs/rcss-matrix

Not a browser engine

No <a href> navigation, no CSS Grid, no fetch, no DOM library you remember. Navigation is the host calling LoadDocument; layout is flexbox and absolute positioning.

RCSS is not CSS

It is RmlUi's style language: a deliberate subset with its own additions. The generated matrix is the exact difference, keyed to the engine in your package — read it before you write a property.

Not every property is drawn

A few parse and are then not rendered — box-shadow is the headline case, and it warns per view and names a substitute. The matrix marks them; nothing here is a surprise you find at ship time.

Source only, so C++ only

There is no Binaries/ directory and that is deliberate. A Blueprint-only project has no toolchain to compile the plugin with — converting takes two minutes: Tools → New C++ Class → None.

VaCuus

HTML/CSS user interface for Unreal Engine, rendered off the game thread.

Issues and questions: github.com/ufna/VaCuus/issues

MIT-licensed RmlUi 6.x and QuickJS-ng are vendored in-tree, each with its license beside it.