The pack
webgpu
function webgpu(): Pack<{ gpu: WebGpuApi; preloader: PreloaderApi }>
The WebGPU backend pack. Draws the worlds' sorted instance tables from the kernel atlas — and each big picture
from its texture of its own, uploaded once with mips — world-space tables in the sprites pass (with world-screen's
underlay among them, through the screen camera) and screen-space ones in screen, publishes a WebGpuBag on ctx.backend.webgpu for passes between them, hands runs of
drawBy objects to their registered drawer in place, splices every
registered deform definition into its grid shader, survives a lost device (boot.contextLost → rebuild →
boot.contextRestored), and claims the screenshot, startRecording, stopRecording and loseDevice commands.
No options. Headless (no document) it attaches and draws nothing.
Functions
installWebGpu
function installWebGpu(k: Kernel, prepared?: GPUDevice, deferRegistration = false): { gpu: WebGpuApi; initialise(): Promise<void>; register(): void }
Install one backend's passes and commands; the caller controls device initialisation.
mipLevels
function mipLevels(width: number, height: number): number
Mip levels for a texture of its own, down to 1×1: a 1584×2816 picture has 12.
placeFrame
function placeFrame(assets: Pick<KernelAssets, 'size' | 'framePlace' | 'ownTexture'>, frame: number, out: Float32Array, o: number): number
Write a frame's UV rect at out[o..o+3], half-texel inset, in the texture the frame lives in — the atlas, or a
picture's own texture, whose UVs run over the picture rather than the atlas — and return that texture's id (0 the
atlas). The one place both backends turn a frame index into texture coordinates.
runsOf
function runsOf(blend: Uint8Array, deformKind: Uint8Array, flags: Uint8Array, count: number, base: number, tex?: Uint32Array): Run[]
Split a table into runs of consecutive instances sharing (blend, deformed, drawBy, texture) — draw order is
untouched, draw calls grow only where modes mix. tex is each instance's texture (framePlace(frame).tex; absent,
all atlas): atlas sprites batch as they always did, and a picture in a texture of its own is a run of its own.
base offsets first.
spriteParts
function spriteParts(tables: ReadonlyArray<InstanceTable>, splits: ReadonlyArray<WorldSplit>, floors: ReadonlyArray<Pick<WorldFloor, 'through'>>): SpritePart[]
Each table's part: its space and pass, a 'world-split' (the world layers above it are 'over') and a 'world-floor' (the world layers up to its own are 'floor').
worldSplit
function worldSplit(tables: ReadonlyArray<InstanceTable>, splits: ReadonlyArray<WorldSplit>): number
Which sprite pass draws each table: the index in tables of the first world layer a split names; -1 for none.
Types
KernelAssets
interface KernelAssets
The kernel atlas surface a backend reads. Mirrors KernelAssets without importing the implementation.
readonly size: number— Side of the square atlas texture in pixels.readonly image: OffscreenCanvas | HTMLCanvasElement | null— The canvas the atlas is drawn on; null when there is none (headless).readonly version: number— Counter that moves whenever the atlas pixels change; the backend uploads the atlas again when it differs from the one it last saw.frameRect(index: number): { x: number; y: number; w: number; h: number }— A frame's rect in the atlas; a frame in a texture of its own answers the white square (and warns once).framePlace(index: number): { x: number; y: number; w: number; h: number; tex: number }— Where a frame's pixels are:tex0 the atlas, elseownTexture(tex); the rect is in that texture's pixels.readonly ownTextures: ReadonlyArray<{ readonly id: number; readonly key: string; readonly width: number; readonly height: number; readonly image: ImageBitmap | OffscreenCanvas | HTMLCanvasElement | null; readonly live: boolean; readonly version: number; readonly page?: boolean }>— Later atlas pages (page) and big pictures in textures of their own, in the order they landed; id = position + 1.imageis null headless and after destroy. Aliveone (every page) is a canvas to upload again wheneverversionmoves.ownTexture(id: number): { readonly width: number; readonly height: number; readonly page?: boolean } | null— A texture of its own by id; null for 0, the atlas's first page.takeDirty(): { x: number; y: number; w: number; h: number }[]— The first page's rectangles (pixels) changed since the last call; reading them clears them, so each is uploaded once.takeDirtyPages?(): ReadonlyArray<{ readonly tex: number; readonly since: number; readonly version: number; readonly rects: ReadonlyArray<{ x: number; y: number; w: number; h: number }> }>— Each later page's rectangles drawn since the last call, with the page version they start from and the one they bring it to; reading them clears them. A copy atsincetakes the rects; any other waits for the whole page. Absent from a kernel older than atlas pages.
Run
type Run = [number, number, number, boolean, number, number]
A run of consecutive instances sharing (blend, deformed, drawer, texture): [first, count, blend, deformed, drawBy, tex]. tex 0 is the atlas.
SpritePart
type SpritePart = 'floor' | 'world' | 'over' | 'screen'
The sprite pass a table is drawn in: 'floor' and 'world' both in 'sprites' (floor first), 'over' in 'sprites-over', 'screen' in 'screen'.
WebGpuApi
interface WebGpuApi
game.gpu: the backend's readable facts. Nothing to call.
readonly ready: boolean— True once the device and pipeline exist; false before boot, in a browser without WebGPU, and while a lost device is being replaced.readonly drawCalls: number— Instanced draws issued last frame: one per run of a layer sharing a blend mode, deform flag, drawer and texture.readonly losses: number— How many times the device has been lost and rebuilt this session. Every loss emitsboot.contextLost, every rebuildboot.contextRestored.
WebGpuBag
interface WebGpuBag
What this backend publishes on the pass context for packs that draw after it (lighting, post, effects).
device: GPUDevice— The GPU device every later pass creates its pipelines and buffers on. It CHANGES after a device loss: a pack keyed on it must rebuild (listen toboot.contextLost).format: GPUTextureFormat— The swapchain's texture format; a pass's colour target must match it.context: GPUCanvasContext— The canvas context;context.canvasis the element (fortoBlob,captureStream).encoder: GPUCommandEncoder— This frame's command encoder; each pack opens its own render pass on it with loadOp 'load'. Submitted atpresent.view: GPUTextureView— This frame's colour target view; render passes attach to it.texture: GPUTexture— The swapchain texture itself (COPY_SRC | TEXTURE_BINDING), for a post chain that reads the frame back.atlas: GPUTextureView | null— The kernel atlas as a texture view, for packs that sample sprites themselves (effects, a background image). Null until the atlas has been uploaded.textureOf(tex: number): GPUTextureView | null— The texture a frame lives in, by itsframePlace(i).tex: 0 isatlas, anything else a big picture's texture of its own. Null until that texture is uploaded. A pack that samples a loaded image itself binds this rather thanatlas, and then draws pictures as well as sprites.sampler: GPUSampler— Linear atlas sampler; pixelArt uses texel snapping with a derivative-sized antialias seam.width: number— Physical pixels of the target, wide.height: number— Physical pixels of the target, high.logicalWidth: number— Logical pixels wide (the screen policy). Fragment coords are physical: scale = width / logicalWidth.logicalHeight: number— Logical pixels high (the screen policy).stats: { drawCalls: number; instances: number; textures: number; textureBytes: number }— What this backend spent last frame, for anything that profiles: draws, instances, textures bound and their bytes.
WorldFloor
interface WorldFloor
A registry entry (kind 'world-floor') from a pack that acts between a 2D world's floor and the pieces on it, as
lighting-2d's floor does for its under lights: inside 'sprites', the world-pass tables up to and including the 2D
world layer through are drawn first, then between runs, then the rest. Both renderers list the kind 'render-feature'
entry { name: 'world-floor' } when they honour it.
readonly name: string— The pack that asked, for messages: 'lighting-2d'.readonly through: string— The last 2D world layer that is floor.between(ctx: PassContext): void— Called every frame between the floor and the pieces, with the frame's context (its backend bag).
WorldSplit
interface WorldSplit
A registry entry (kind 'world-split') from a pack that composites over only part of the world's stack, as
lighting-2d's unlitAbove does: the world-pass tables after the 2D world layer above are drawn in the
'sprites-over' pass, after that pack's own pass and before post, instead of in 'sprites'.
readonly name: string— The pack that asked for the split, for messages: 'lighting-2d'.readonly above: string— The last 2D world layer drawn in 'sprites'; the world layers above it are drawn in 'sprites-over'.


