← The API

Phaser AE

API docs

v3.0.0-2206 exports in 26 modulesreleased October 19, 2026

ContentsRendering contract

Need to get started? Install the Phaser AE CLI →

On stage

Rendering contract

The world/backend contract: instance tables, aggregates, deformers, drawers

engine/kernel/src/render.ts

Types

Aggregate

interface Aggregate

From @phaser-ae/kernel

ONE roster object, MANY instances: a tilemap, a particle pool. The object sits in its layer at its depth like any other; when the gather reaches it, it emits — only what the view needs. The roster stays small (one handle), the table stays exact (every drawn instance).

  • readonly count: number — Instances it holds in total (what h.aggregate.count reports), drawn or not.
  • emit(out: InstanceSink, alpha: number, view: ViewRect): void — Append the instances inside view to out, interpolated at alpha. Called once per frame when the object is visible. 2D worlds.
  • emit3d?(out: InstanceSink3d, alpha: number): void — The 3D face: append mesh instances, interpolated at alpha. A 3D world calls this instead of emit; an aggregate that has no 3D form leaves it out and is skipped.

BackendInfo

interface BackendInfo

From @phaser-ae/kernel

A backend pack's declaration of what it can draw.

  • readonly draws: ReadonlyArray<DrawKind> — The table kinds it can draw; a world needing another is a bake error.

DeformDef

interface DeformDef

From @phaser-ae/kernel

A CUSTOM vertex deformer as data: a deform-* pack registers one under the registry kind 'deform' and objects request it by name (deform: { kind: 'curl' }). code runs in the VERTEX stage of the 24×64 grid with in scope: p (vec2f, the vertex in world units, centred on the object, y down — mutate it), shade (f32 brightness multiplier, 1 lit — mutate it), uvn (vec2f 0..1 across the object), size (vec2f world units), amount (f32), t (f32: phase + speed·time·2π), TAU, d.uv (vec4f the frame's atlas rect) and tex/samp for textureSampleLevel(tex, samp, uv, 0.0) in the vertex stage. The optional fragment runs per pixel after the base sample with color (vec4f straight-alpha, base = texel × tint × shade — mutate it), tint, uvn, uvRect, t, amount, shade, kind, tex/samp. Every backend that draws sprites2d splices every registered def in.

  • name: string — The name objects request as deform.kind. Unique per composed game.
  • description?: string — One line saying what it looks like.
  • code: string — WGSL, vertex stage: displaces p, may set shade.
  • fragment?: string — WGSL, fragment stage, optional: mutates color.

DrawerDef

interface DrawerDef

From @phaser-ae/kernel

A pack that draws some objects ITSELF (registry kind 'drawer'): the backend calls draw for every run of instances whose drawBy equals id, inside its sprite pass, in sorted order. The effects pack is one.

  • name: string — The registry name: the pack's id.
  • id: number — The value objects carry in drawBy; 1..255, unique per composed game.
  • draw(pass: unknown, table: InstanceTable, first: number, count: number): void — Draw count instances of table starting at first, into the backend's open pass. pass is backend-typed (a GPURenderPassEncoder on WebGPU).
  • drawGl?(bag: import('./webgl.js').WebGl2Bag, table: InstanceTable, first: number, count: number): void — Optional GL entry point, invoked in the same sorted position with the shared frame surface.

DrawKind

type DrawKind = 'sprites2d' | 'meshes3d' | 'vectors' | 'text'

From @phaser-ae/kernel

A kind of instance table, and so what a backend must be able to draw.

InstanceSink

interface InstanceSink

From @phaser-ae/kernel

What a 2D gather offers an aggregate: append instances to the current layer's table.

  • add(x: number, y: number, width: number, height: number, frame: number, tint: number, rot?: number, sx?: number, sy?: number, alpha?: number, blend?: number): void — One instance: centre, size, frame, tint (0xRRGGBBAA), then optionally rotation (radians), scale x/y, alpha (0..1) and blend index. Defaults: 0, 1, 1, 1, 0.

InstanceSink3d

interface InstanceSink3d

From @phaser-ae/kernel

What a 3D gather offers an aggregate: append mesh instances to the current layer's table. A particle system is the reason this exists — one roster object, thousands of billboards, drawn in the object's own sorted place by the renderer that is already there.

  • add(x: number, y: number, z: number, width: number, height: number, frame: number, tint: number, alpha?: number, roll?: number, blend?: number, billboard?: number, dx?: number, dy?: number, dz?: number): void — One instance: world centre, the quad's size in world units, atlas frame, packed rgba8 tint, then optionally opacity, in-plane roll (radians, about the view axis), blend index, and the billboard mode — 0 none, 1 faces the camera, 2 upright, 3 aligned to the direction dx dy dz (a stretched spark, a raindrop), 4 flat, in the plane whose normal is dx dy dz (up, the XZ plane, unless given: a ripple on the ground, a shockwave on a wall). Defaults: 1, 0, 0, 1, and no direction.

InstanceTable

interface InstanceTable

From @phaser-ae/kernel

One layer's instances, sorted, in columns indexed by draw position. Written fresh each frame by a world's gather.

  • readonly layer: LayerName — The layer this table draws.
  • readonly kind: DrawKind — What the backend must draw it with.
  • readonly space: 'world' | 'screen' — 'world' tables go through the camera; 'screen' tables are in logical pixels (HUDs, menus).
  • readonly pass?: 'world' | 'screen' — Which of the backend's two sprite passes draws it, when that is not its space: 'world' is drawn in place among the world's layers, before anything lights or post-processes the world; 'screen' after all of that. A 'screen' space table in the 'world' pass is world-screen's underlay — fixed to the screen, yet over the world's backdrop and under its pieces. The table's place in the frame's list is still the order; the backend never reorders.
  • readonly count: number — Instances in the table; the arrays may be longer.
  • readonly order: Uint32Array — Slot indices in draw order.
  • readonly x: Float32Array — Centre x, world units (or logical px for 'screen'), interpolated.
  • readonly y: Float32Array — Centre y, interpolated.
  • readonly z: Float32Array — z, not interpolated; 3D worlds.
  • readonly rot: Float32Array — Rotation in radians, interpolated along the shortest arc.
  • readonly sx: Float32Array — Scale x; a sign flip is a mirror and is not interpolated through zero.
  • readonly sy: Float32Array — Scale y; same rule as sx.
  • readonly width: Float32Array — Width in world units before scale.
  • readonly height: Float32Array — Height in world units before scale.
  • readonly frame: Uint32Array — Atlas frame index.
  • readonly tint: Uint32Array — Packed rgba8 tint (0xRRGGBBAA).
  • readonly flags: Uint8Array — The object's drawBy: 0 for the backend's sprite path, else a registered drawer's id. An aggregate's instances carry their object's.
  • readonly alpha: Float32Array — Opacity 0..1, multiplied with the tint alpha.
  • readonly blend: Uint8Array — Blend mode index into BLEND_MODES.
  • readonly repeatX: Float32Array — Texture repeats across the quad's width; 1 = once.
  • readonly repeatY: Float32Array — Texture repeats across the quad's height; 1 = once.
  • readonly scrollX: Float32Array — UV scroll offset x, in frame fractions.
  • readonly scrollY: Float32Array — UV scroll offset y, in frame fractions.
  • readonly deformKind: Uint8Array — Deform kind index into DEFORM_KINDS; 0 = none.
  • readonly deformAmount: Float32Array — Deform amount, a fraction of the quad's size.
  • readonly deformSpeed: Float32Array — Deform speed in cycles per second.
  • readonly deformPhase: Float32Array — Deform phase offset in radians.
  • readonly mesh?: Uint32Array — 3D worlds: mesh id per instance; the renderer batches by it.
  • readonly pitch?: Float32Array — 3D worlds: interpolated pitch about x, radians (rot carries yaw).
  • readonly roll?: Float32Array — 3D worlds: interpolated roll about z, radians.
  • readonly sz?: Float32Array — 3D worlds: interpolated scale z.
  • readonly billboard?: Uint8Array — 3D worlds: 0 draws with the object's own rotation, 1 turns the mesh to face the camera, 2 turns it about y only (a tree).
  • readonly material?: Uint32Array — 3D worlds: which registered material this instance wears; 0 is the default one. A material carries the things a PBR surface needs beyond its colour — metallic, roughness, emissive, and the normal and metallic-roughness maps — and it is an INDEX rather than a dozen more per-instance floats, because most objects share a handful of materials and a particle field shares one.
  • readonly surface?: Uint32Array — 3D worlds: which EXTENDED SURFACE slot this instance wears, and 0 for none — a bend, a back face, an alpha cutout, decal artwork, a surface effect. It is a slot rather than the sixteen floats those need, because a mesh that wants none of them is the overwhelming majority and must pay nothing.
  • readonly pickable?: Uint8Array — 3D worlds: whether this instance answers render3d.pick. It is OPT-IN — clicking scenery should select nothing, and a world where every prop is clickable is a world where the player selects the floor.

SpriteCutoutSource

interface SpriteCutoutSource

From @phaser-ae/kernel

The sprite-cutout pack's per-object threshold, registered under its own name so backends can read it without every game carrying the column. Indexed by roster slot (InstanceTable.order); an aggregate's instances use their object's. Zero or non-finite disables it; renderers clamp a positive threshold to at most 1 and make the pixels that survive the cut opaque.

  • name: string — Registry entry name, sprite-cutout.
  • thresholds(): Float32Array — Current threshold column; resolve again after roster growth.

ViewRect

interface ViewRect

From @phaser-ae/kernel

The camera's view this frame, in world units: top-left + size, and the rendered camera itself.

  • x: number — Left edge, world units.
  • y: number — Top edge, world units.
  • w: number — Width, world units.
  • h: number — Height, world units.
  • cx: number — The centre, world units — x + w / 2. It exists because w / 2 is NOT it, and the difference is invisible in a wide window: a narrower one letterboxes, so x is where the visible slice starts and w / 2 is off by half the letterboxing. Twenty demos centred on w / 2 and were perfectly centred in a wide pane while a whole grid sat off the left edge in a tall one.
  • cy: number — The centre, world units — y + h / 2. See cx.
  • camX: number — The rendered camera's x.
  • camY: number — The rendered camera's y.
  • zoom: number — The rendered camera's zoom.

World

interface World

From @phaser-ae/kernel

A world pack's side of the frame: it orders and gathers the roster into instance tables, and owns the camera transform. Registered with Kernel.registerWorld.

  • readonly info: WorldInfo — Its declaration.
  • readonly camera: Camera — The camera it transforms through (the kernel's record).
  • resolveDepth(): void — Write depth for every live object from layer + transform. Called each frame before sorting.
  • tables(alpha: number): ReadonlyArray<InstanceTable> — Emit this frame's sorted, interpolated tables, after resolveDepth.
  • afterStep?(dt: number): void — After every fixed step, once all simulate steps have run (a wrapping world brings positions into range here).

WorldInfo

interface WorldInfo

From @phaser-ae/kernel

A world pack's declaration.

  • readonly kind: '2d' | '3d' | 'screen' — Which space the world simulates in.
  • readonly needs: ReadonlyArray<DrawKind> — The table kinds it emits; the backend must draw every one.
  • readonly layers: ReadonlyArray<LayerName> — Its layer names. The kernel numbers them after every earlier world's, and a spawn that names no layer lands on the first. Back to front, except where a world says otherwise: world-screen declares its underlay last so a screen-only game's unnamed spawns still land on hud. Draw order is the order of the tables the world returns.