---
title: "Rendering and spatial UI"
description: "Quartz UI 0.57.0-dev: Rendering and spatial UI. Source-reviewed guidance, usage and limitations."
status: approved
visibility: public
sourceRevision: fe5b709ec900e282b50e58b819a25041945005f9
reviewedAt: 2026-09-24
---

# Rendering and spatial UI

> **0.57.0-dev · beta source documentation.** Native, Blueprint, Editor and rendering examples are source-reviewed, not executed in Unreal during this scan. See [validation and limits](https://betterbuilt.games/docs/quartz-ui/evidence).

Choose the presentation path before tuning raster size. Each browser has a rendering cost; a browser per unit marker is usually the wrong architecture. None of these APIs changes gameplay authority.

| Path | Ownership and purpose | Current boundary |
|---|---|---|
| UMG/Slate screen UI | widget presents one logical view | Raster follows actual Slate geometry/DPI |
| Direct world surface | static mesh samples stable browser texture | Passive; Win64 D3D11/D3D12 capability gate |
| Compatibility spatial widget | UWidgetComponent presents browser through its render target | Passive; extra world-widget composition |
| DOM projected markers | one page renders bounded position snapshots | Browser cadence adds latency |
| Retained HTML projection | native render view positions HTML atlas pixels | Experimental; fixed-cell atlas and limited interaction |

## Scene backdrop

For a transparent screen-space browser, mark a panel:

```html
<aside data-quartzui-backdrop="frost"
       style="backdrop-filter:blur(24px);border-radius:18px;background:#15203344">
  <h2>Inventory</h2>
</aside>
```

The native observer maps its geometry to the exact local player's viewport and filters the post-tonemap scene before browser composition. `SetSceneBackdropEnabled(false)` disables it per widget. A standalone Slate host must supply its owning LocalPlayer. No scene capture or CPU readback is needed for this effect.

Bounds: eight rounded rectangles, blur 0–64 CSS pixels, screen-space SDR. DOM-behind-DOM blur stays normal CSS. Separate browser views cannot blur each other. Arbitrary filter chains, transformed clipping, HDR/stereo, world-space blur, and a fully qualified split-screen rendering matrix are not established. Hiding retained panels must remove their regions; the shipped observer tests cover hidden ancestors and in-flight updates.

## A passive panel in the world

Add **QuartzUI Direct World Surface** (`UQuartzUIWorldSurfaceComponent`) to an actor. Its defaults supply an engine plane and pass-through material. Set Raster Size, then call `InitializeApp(AppAsset, LocalPlayer)` for normal project contracts/providers. A custom material needs a Texture2D parameter whose name matches Texture Parameter Name (default `SlateUI`).

The mesh transform controls physical size; raster pixels control sharpness. The default engine plane is 100×100 units before scale. Match aspect ratios explicitly. `AttachToView(View)` instead borrows an existing unpresented view; `InitializeStandalone(Definition)` supplies only the core contract. Use exactly one path.

`UQuartzUISurfaceTexture` is a stable facade over Epic's existing browser texture. Do not recreate it every frame. The component owns heartbeat, resizing, binding generations, visibility suspension, and teardown. `ReleaseQuartzUI` releases its presentation/session; owned versus borrowed view behavior follows the initialization path. Auto-suspension normally begins when hidden or not recently rendered, with lower-frequency visibility polling. Providers are refreshed when revealed.

Transparent browser output is premultiplied alpha. Prefer a matching AlphaComposite material; inspect UV orientation and gamma on target hardware. Sizes are capped by both axis and project memory budgets; avoid continuously resizing with distance.

The transfer is reported as `BackendManaged`, not zero-copy. The facade adds no second full-size copy, but Epic's backend may copy/synchronize. D3D12 historical host evidence is recorded upstream; D3D11 remains runtime-unvalidated there due to a stock host/device failure. This scan ran neither RHI. Input rays, mesh-UV hit mapping, keyboard/IME, and multi-user focus are not implemented by the direct surface.

## Compatibility spatial widget

Use `UQuartzUIWidgetComponent` only when the standard UWidgetComponent path is needed. Initialize with the same app/view/standalone choice. World space uses authored world geometry and draw size; screen space projects the anchor to a player widget layer. It disables hardware interaction and hit testing. Suspend it explicitly when pooling/hiding it and release its browser on owner teardown. Budget the browser raster and the additional UWidgetComponent render target separately.

## Project world points into one DOM

`FQuartzUISpatialProjection` and `UQuartzUISpatialBlueprintLibrary::ProjectWorldLocations` accept the exact PlayerController, a bounded point array, and `FQuartzUISpatialProjectionSettings`. They return one `FQuartzUISpatialProjectionResult` per input index, including invalid/behind-camera entries. Use projection success/on-screen flags before displaying records.

Minimal Blueprint flow: after camera updates, **Project World Locations** → pair results with your stable IDs → publish a compact struct snapshot → update keyed web elements. Choose physical pixels or DPI-converted viewport/widget coordinates once; do not divide by DPI twice. Separate high-frequency positions from ordinary inventory data. `FlushOutboundMessagesNow` can reduce one native tick delay but cannot make asynchronous Chromium paint synchronous.

## Experimental retained HTML projection

`FQuartzUIHtmlProjection` accepts Player, owning View, a permitted page URL, atlas dimensions, cell pixels, and an interactive flag. Its separate document needs its own normal contract handshake. Create it once in a native owner; call `Update` after pose updates with copied `FQuartzUIHtmlAnchor` values; destroy it before the player/view lifetime ends.

Minimal recipe: a 2×1 atlas with 64×64 cells paints normal/hover artwork; add one anchor with a stable Key, world Position, and cell indices 0/1. For passive nameplates set `bInteractive=false`. `PublishContent` sends retained `projection.content` only when artwork metadata changes; world positions belong in `Update`, not repeated browser messages. `SetCallbacks` receives validated activation/hover keys for the interactive path.

Limits: at most 128 anchors; uniform bounded cells; no world occlusion, general DOM partitioning, differently sized cells, or full input per cell. Browser Ready and frame ID do not prove a particular content revision is painted. The host must manage allocation/repaint consistency; never reuse a live cell for another identity without a strategy. This prototype is an opt-in advanced page, not a general production replacement for the shell.

## Measure what the user sees

Use `stat QuartzUI` for plugin counters, Unreal Insights/GPU profiling for composition, and Chromium tools for page cost. Surface estimates are not full GPU/process memory. The development FrameProbe measures composed-backbuffer timing and optional frame capture, not monitor scanout. Historical benchmark figures in the original docs are not promises for this revision or hardware.

Rendering guide (source: `Documentation/Rendering.md`), direct surfaces (source: `Documentation/DirectWorldSurfaces.md`), projection (source: `Documentation/HtmlProjectionPrototype.md`). All rendering recipes require host/RHI validation; none were rendered during this scan.

Canonical HTML: https://betterbuilt.games/docs/quartz-ui/rendering
