Skip to content

UIRenderer ​

The UIRenderer is an ActorComponent that provides an orthographic 2D overlay on top of the 3D scene. It creates its own OrthographicCamera and a Three.js CSS2DRenderer so that UINode and UISprite elements are always drawn screen-aligned, independently from the main 3D camera.

Typically a single UIRenderer is registered once on a dedicated actor (e.g. "camera2D"). Every UINode created under that actor auto-discovers the renderer and registers itself.

ts
import { Actor, UIRenderer, UISprite } from "@jolly-pixel/engine";

const camera2D = new Actor(world, { name: "camera2D" })
  .addComponent(UIRenderer, { near: 1 });

How it works ​

  1. On construction the component creates an OrthographicCamera sized to match the current canvas and appends a CSS2DRenderer DOM element on top of the WebGL canvas.
  2. It listens to the game renderer's resize event and automatically updates both the CSS overlay size and every registered node position.
  3. On every draw event it renders the scene through the orthographic camera so that CSS2D objects (used by UIText) stay in sync with the 3D world.

Constructor ​

ts
interface UIRendererOptions {
  /** Near clipping plane of the orthographic camera. */
  near?: number;
  /** Far clipping plane of the orthographic camera. */
  far?: number;
  /** Z position of the camera (draw order). */
  zIndex?: number;
}
OptionDefaultDescription
near0.1Near clipping plane
far2000Far clipping plane
zIndex10Camera Z position — controls draw order

Creating a HUD element ​

Once a UIRenderer exists, child actors can register UISprite components that are automatically positioned relative to the screen edges.

ts
const button = new Actor(world, {
  name: "startButton",
  parent: camera2D
})
  .addComponentAndGet(UISprite, {
    anchor: { x: "center", y: "top" },
    offset: { y: -80 },
    size: { width: 200, height: 60 },
    style: { color: 0x0077ff },
    styleOnHover: { color: 0x0099ff },
    text: {
      textContent: "Start",
      style: {
        color: "#ffffff",
        fontSize: "20px",
        fontWeight: "bold"
      }
    }
  });

button.onClick.connect(() => {
  console.log("Start clicked!");
});

Updating positions at runtime ​

When you change the offset or anchor of a node after creation you can force a full re-layout by calling updateWorldPosition:

ts
const uiRenderer = camera2D.getComponent(UIRenderer)!;
uiRenderer.updateWorldPosition();

Cleanup ​

Calling clear() destroys every registered node, removes the CSS overlay from the DOM, and unsubscribes from renderer events. Destroying the component calls it too:

ts
uiRenderer.clear();

API ​

ts
class UIRenderer extends ActorComponent {
  /** The renderer registered for this world, if any. */
  static for(world: World): UIRenderer | undefined;
  camera: THREE.OrthographicCamera;
  nodes: UINode[];

  addChildren(node: UINode): void;
  removeChildren(node: UINode): void;
  updateWorldPosition(): void;
  clear(): void;
}

See also ​