Your game's interface in HTML and CSS, rendered entirely off the game thread.
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.

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.
| 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 avg | 1.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 |
.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.
Content/DevUI. vacuus.ReloadUI does the same thing from the console. 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.
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.
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.
The full brief. Which mistakes warn, which are drawn wrong in silence, and how to tell the two apart.
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.
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.
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.



:hover/:active/:focus, a wheel-scroll list, a text field with tab order, and a region that lets clicks reach the gameEvery other web-UI plugin for Unreal on Fab is a Chromium wrapper — CEF under one name or another — so this is the comparison that decides the purchase. Here is what a browser costs inside a game, beside what this costs, with both columns measured.
| Added to the build | 330–390 MB shipped payload |
|---|---|
| Runtime RAM | 150–300+ MB |
| Processes | 4–5 separate, plus their IPC |
| Input path | +1–2 frames across the process boundary |
| Game thread | pumps the browser's message loop CefDoMessageLoopWork |
| Upkeep | a two-week Chromium security cycle |
| HTML/CSS core on disk | 256 MB Chrome-class, stripped |
And in UE specifically: the message-loop pump above was found in Epic's own integration, and accelerated off-screen rendering on Linux/NVIDIA is broken.
| Added to the build | +3.22 MiB Linux Shipping proxy |
|---|---|
| Runtime RAM | +14.3 MB A/B median |
| Processes | 0 extra in-process, one UI thread |
| Input path | answered in-process from the frame's snapshot |
| Game thread | 0.012 ms / frame enqueue input, read snapshot |
| Upkeep | no Chromium in your build |
| HTML/CSS core on disk | 3.8 MB the vendored RmlUi library |
Disk, RAM and game-thread rows are the plugin's own budget table, re-runnable in your project: vacuus.RefHud then vacuus.M1HUD.PerfLog 1. The game-thread figure is the dev build; cooked Shipping measures 0.008–0.009 ms.
Dropping the browser is not dropping motion. RCSS has transitions and @keyframes, decorators — linear, repeating, radial and conic gradients, plus a built-in shader — backdrop blur over the live 3D scene, and updates driven straight from your UPROPERTY fields, all inside a deterministic in-process core.
The screenshots directly above this section are not mockups and not a competitor's demo reel: they are this pipeline rendering itself, headless, at 1920×1080.
An agent can only build a UI it can hold in its head. The supported language here is small, closed and generated — 99 properties, 20 shorthands, 16 decorators, parsed out of the exact RmlUi vendored in your package — so the whole surface fits in an agent's context as ground truth. A mistake surfaces as a log warning naming file:line, in every configuration including Shipping, instead of a devtools session nobody is sitting in front of; a screen verifies with one headless render.
That is why an agent can build flashy, animated UI here: the surface is bounded and instrumented. Against a full browser it is guessing across the whole web platform, and it cannot see the screen it guessed wrong on.
How that works → 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.
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.
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.
A Fab install lands in the engine, with binaries Epic built — a Blueprint-only project can enable it and go. The full source ships regardless; drop it into <Project>/Plugins instead and it builds like any C++ plugin (that route does need a C++ project).
VaCuus is source-available under the Business Source License 1.1. In one sentence: everything is free until you ship something commercial, and a commercial release is $69.99 per project. No signup, no licence key, nothing in the plugin phones home.
$0no signup, no licence key
Everything short of a commercial release, with no time limit and nothing withheld.
GitHub Releases — one archive per engine version, with SHA256SUMS.txt beside them.
Not a trial. It is the same plugin as the paid one, and it does not stop working.
$69.99per project · perpetual
Needed when you ship or operate a product for commercial advantage or monetary compensation.
Consoles, source escrow or purchase-order terms — ufna@ufna.dev
Nothing to activate. There is no licence key and no check in the plugin — the copy you develop with is the copy you ship.