Skip to content

Runtime architecture ​

Runtime puts the engine in a browser page. It creates the renderer and world for a canvas, prepares assets, and drives the world from a scheduled game loop. The web and Electron guides use the same runtime code.

What the runtime composes ​

Runtime owns the wiring; each part keeps its behavior in its own package. The sections below follow one part at a time.

PartRuntime's roleOwner of the behavior
Canvas and rendererResolve the canvas and create ThreeRendererEngine renderer
AssetsResolve the catalog and add custom loaders to the engine defaults@jolly-pixel/asset and the engine loaders
ScenesAwait the initial scene during load()Engine SceneManager
FramesFeed renderer animation callbacks to world.tick()@jolly-pixel/loop and World
Browser UIMount the loading screen, overlays, and readoutsRuntime and @jolly-pixel/ui

Assets ​

Runtime.create() fetches and parses a catalog URL once; no catalog means an empty one. Every loader shares the Three.js LoadingManager exposed as runtime.manager. Runtime loads the startup batch itself; the engine loads scene assets.

Creation ​

create() rejects on an invalid canvas target, a failed catalog request or manifest, or a renderer failure. The catalog resolves first, so a failed request rejects before any renderer exists. Nothing runs until load() or start().

Startup ​

GPU detection sets the FPS cap and, unless the renderer options fix it, the pixel ratio. Without a screen, the entrance, delay, and completion steps do nothing. Any failure rejects load(), leaves the runtime stopped, and is shown on the screen when one is mounted.

Scene loading ​

The engine owns this flow. Runtime awaits it for the initial scene and forwards its progress to the loading screen. A ready scene becomes active on the next beginFrame(), so load() can resolve before the scene's awake() runs.

Frame path ​

FrameScheduler, inside GameLoop, decides the fixed step count and whether to render; the maxFps cap skips rendering while fixed steps still run. World.tick() updates input even when no fixed step is due.

Metrics ​

One recorder feeds every display. RuntimeMetrics registers the renderer counters, latches them on each draw, and owns the readout panel. The recorder and the counters exist even when nothing is mounted.

Lifecycle ​

start() focuses the canvas, attaches the focus listeners, focus hint, and view helper, then connects the world and starts the loop. stop() stops the world and loop and releases everything start() attached. Both are idempotent; dispose() also removes the HUD, readout panel, overlay layer, and renderer resources.

Browser surfaces ​

The overlay layer holds what is drawn above the canvas and never takes pointer events itself. The view helper renders into the canvas after each draw. The loading screen mounts in loadingContainer, outside the overlay.

Details: runtime API, metrics, overlay layer, asset options, scenes and assets, loading screen, and frame scheduling and performance.