HUD overlay
Compose gameplay and UI layers without coupling scene logic.
HUD overlay
A HUD (heads-up display) renders UI elements on top of the game world — health bars, score text, mini-maps, debug readouts — without those elements moving or scaling with the game camera. The recipe separates world-space content from screen-space content using scene.ui, a screen-fixed layer that every scene owns and that is automatically rendered above the scene’s world content.
Approach
Add HUD elements directly to scene.ui instead of creating a separate overlay scene. The world (game nodes, sprites, graphics) lives under scene.root; the HUD (labels, progress bars, panels) lives on scene.ui and is composited on top automatically — no extra scene or stack needed.
class GameScene extends Scene {
init() {
this._angle = 0;
// HUD elements on scene.ui — screen-space, always on top.
this._scoreText = new Label('SCORE 1240', { fontSize: 22 });
this._scoreText.anchorIn(this.ui, 'top-left', 18, 14);
this.ui.addChild(this._scoreText);
this._health = new ProgressBar({ width: 220, height: 8, value: 1 });
this._health.anchorIn(this.ui, 'top-left', 18, 48);
this.ui.addChild(this._health);
}
update(delta) {
this._angle += delta.seconds * 90;
}
draw(context) {
context.backend.clear(new Color(20, 32, 58));
// ... draw game world ...
}
}
Start the scene as normal — scene.ui is rendered automatically:
const game = new GameScene();
await app.start(game);
Why scene.ui, not manual draw-order
The alternative — rendering HUD elements after the world in a single scene’s draw method — works for simple cases. scene.ui gives you:
- Screen-space anchoring via
widget.anchorIn(this.ui, 'top-left' | 'center' | 'bottom-right' | …, offsetX, offsetY)— widgets stay pinned to corners or the center regardless of canvas size. - Hit-testing in screen space — UI widgets are clickable and focusable without needing camera-inverse transforms.
- Independent lifecycle — widgets added in
initare automatically cleaned up when the scene is destroyed.
Mini-map with a mask
A mini-map is a special HUD element: it needs a second View to render the world from a zoomed-out perspective into a RenderTexture, which is then displayed as a sprite in the corner of the HUD. The capture is a CallbackRenderPass with a { target }, run once per frame from the scene’s pipeline:
// In GameScene.init: build the capture pass once.
const miniRt = new RenderTexture(260, 260);
const capturePass = new CallbackRenderPass(
(context) => {
// Render the world from a wide view into miniRt (immediate-mode draws via context.backend)
this.drawWorld(context.backend);
},
{ target: miniRt }, // redirects the callback's output off-screen; clears the target first if you pass `clear`
);
// Add the mini-map sprite to scene.ui so it stays in screen space.
const miniSprite = new Sprite(miniRt);
miniSprite.anchorIn(this.ui, 'top-right', -280, 20);
this.ui.addChild(miniSprite);
// Clamp to a circle with a mask
const mask = new Graphics();
mask.fillColor = Color.white;
mask.drawCircle(130, 130, 120);
miniSprite.mask = mask;
The CallbackRenderPass with { target } redirects the callback’s drawing into the texture; inside a target redirect, issue immediate-mode draws through context.backend (a context.render(node) there would reset the view back to the camera). The mask clips the mini-map to a circle. See Render targets and Masks for details.
When DOM UI is a better fit
In-canvas HUD works well when the UI is tightly coupled to game state — health bars that react to damage, score text that animates, mini-maps that track player position. For static UI with complex layout (settings menus, inventory grids, text-heavy dialogs), standard HTML/CSS rendered as DOM overlays on top of the canvas is often simpler. The two approaches coexist: the canvas handles the game, DOM handles the menus. Focus and event routing between them requires coordination (pointer-events: none on the DOM when the game needs the pointer), but the architectural separation is clean.
Examples
A game scene with a rotating ring, a Label and a live ProgressBar on scene.ui — the HUD is always on top with no separate overlay scene.
Where to go next
The next recipe, Camera follow & parallax, covers how to move the camera and create depth with layered parallax.