The pack
backdrops
function backdrops(): Pack<{ backdrop: BackdropsApi }>
The backdrops runtime pack. Adds the backdrops pass (after begin, before meshes3d and sprites) and exposes
game.backdrop. Owns no backgrounds: compose backdrop-* packs for those. Each backdrop gets the
follow-camera's visible world rectangle and the interpolated sim time every frame. Supports WebGPU and WebGL2.
Functions
backdropEase
function backdropEase(e: BackdropEase | undefined): EaseFn
The curve an easing names: the maths pack's table by its own name (easeOutCubic, linear, sine) or by the
title screen file's spelling (cubicOut), or the function itself. Unknown names throw.
backdropScope
function backdropScope(paramKeys: ReadonlyArray<string>): Record<string, string>
What a snippet can see, by type, as the WebGL2 translator is told it: the template's names and every param.
checkBackdrop
function checkBackdrop(def: CheckedBackdrop, translate: (code: string, scope: Record<string, string>) => string, opts: { maxCode?: number } = {}): string | null
Check a backdrop definition the way the runtime takes it, with no GPU: the name, the sixteen-param and four-colour
limits, reserved and redeclared names, while/loop (a loop needs a constant bound or it can hang the GPU), the
length, and the WebGL2 translation (translate is render-webgl2's wgslSnippetToGLSL; it throws naming what it
cannot translate). Returns the problem in one sentence, or null.
registerBackdrop
function registerBackdrop(k: { registry: { add(kind: string, def: BackdropDef): void } }, def: BackdropDef): void
The one-liner a backdrop-* pack's attach is: register def under the registry kind 'backdrop' so
game.backdrop.add(def.name) finds it.
It refuses one mistake on the way in, because the shader cannot: the template already writes
let <param> = pN; for every declared param, so a snippet that opens by writing the same line again is a
WGSL redeclaration and the backdrop never compiles. Caught here it names the param; caught by the driver
it is a wall of "invalid pipeline" every frame.
validateBackdrop
function validateBackdrop(def: BackdropDef, opts: { maxCode?: number } = {}): string | null
Check a backdrop definition the way the runtime will take it, with no GPU: the limits, reserved and redeclared
names, while/loop, the length (default 8000 characters) and the WebGL2 translation. A snippet the translator
refuses is SKIPPED on WebGL2 — which is what a build's screenshots and every player without WebGPU see — so
content written by a tool or a model goes through this before define. Returns the problem in one sentence, or null.
Constants
BACKDROP_RESERVED
const BACKDROP_RESERVED: ReadonlySet<string>
The names the backdrop template declares itself: a param may not take one.
backdropWGSL
const backdropWGSL = (code: string, keys: ReadonlyArray<string> = []): string=> …
The WGSL a backdrop runs as: the template around a definition's snippet (code), with its params named by keys
in order. For tools and tests; the runtime builds it per definition.
Types
BackdropDef
interface BackdropDef
What a backdrop-* pack registers: one full-screen background as data. code has uv (0..1, y down),
px (logical pixels), physicalPx (framebuffer pixels, independent of logical fit), res (logical width, height), view (camera x, y, width, height in world units),
time, p0..p15, c0..c3 (vec4 colours), pointer (vec4: x and y in uv, eased; z 1 while pressed; w seconds
since it last moved), TAU, hash21, noise2, fbm2, and the background image as bg (vec4 at this pixel,
transparent black when none is set) and bgAt(uv) in scope, and leaves its result in color, premultiplied.
See engine/packs/backdrop/backdrops/docs/backdrops.md, "Writing a backdrop pack".
name: string— The namegame.backdrop.add()is called with, e.g.'stars'. Unique per composed game.description?: string— One line for the planner's menu: what it looks like.code: string— WGSL: the fragment body.colorstarts transparent black; leave the premultiplied result in it. Every param is in scope BY NAME as well as by position, so a snippet readssunSizerather thanp2— the same name the API sets.time,res,px,uv,view,bg,c0..c3andTAUare the template's own, and a param may not take one of those names.params: Record<string, number>— The params and their defaults, inp0..p15order. At most sixteen.colors: Record<string, string>— The colours and their default hex strings, inc0..c3order. At most four.
BackdropEase
type BackdropEase = EaseName | `${'sine' | 'quad' | 'cubic' | 'quart' | 'quint' | 'expo' | 'circ' | 'back' | 'elastic' | 'bounce'}${'In' | 'Out' | 'InOut'}` | EaseFn
An easing for tween, show and hide: a name from the maths pack's EASE table ('easeOutCubic',
'linear', 'sine'), the title screen file's spelling of the same curves ('cubicOut', 'sineInOut'), or a
function of 0..1.
BackdropHandle
interface BackdropHandle
One added backdrop in the stack, returned by BackdropsApi.add().
set(param: string, value: number): void— Set one param live. Throws ifparamis not one the backdrop declares, orvalueis not finite. Stops a tween of that param. Set params and colours together, live. Throws on an unknown name or a non-finite number before changing anything. Stops any tween of the params it sets.set(values: BackdropValues): void— Set one param live. Throws ifparamis not one the backdrop declares, orvalueis not finite. Stops a tween of that param. Set params and colours together, live. Throws on an unknown name or a non-finite number before changing anything. Stops any tween of the params it sets.setColor(name: string, hex: string): void— Set one colour live from a hex string (#rgb,#rgba,#rrggbb,#rrggbbaa; missing alpha is opaque). Throws on an unknown name.tween(params: Record<string, number>, opts?: BackdropTweenOptions): Promise<void>— Ease params from where they are toparamsoverdurationseconds of sim time (it pauses with the game). A param already tweening is taken over from where it is. Resolves when it arrives, or when it is replaced, set, or removed. Throws on an unknown name or a non-finite number.show(opts?: BackdropTweenOptions): Promise<void>— Fade in to fully shown (fade1) overduration(default 0.5 s). Resolves when shown, or when interrupted.hide(opts?: BackdropTweenOptions): Promise<void>— Fade out to nothing (fade0) overduration(default 0.5 s); a hidden backdrop is not drawn at all.hide({ duration: 0 })right afteraddstarts it hidden, ready toshow.readonly fade: number— 0..1: how much of it is drawn now (its premultiplied output times this); 1 unless shown or hidden.readonly params: Record<string, number>— Its params' values now, by name (a tween's current point); a fresh object each read.time: number— The seconds its snippet'stimereads now: the sim clock plus this backdrop's own offset (0 atadd). Set it to jump (a tool's scrub, a restart from 0); it then runs on with the clock. Throws when not finite.remove(): void— Remove this instance from the stack. Idempotent; also run by the scene's teardown. Its tweens end.readonly name: string— The backdrop name this handle was added with.
BackdropsApi
interface BackdropsApi
game.backdrop: the background stack. Drawn bottom-up in add order, under the world, with premultiplied blending.
background(key: string | null, fit?: BackgroundFit): void— Set (or clear, with null) the BACKGROUND IMAGE the stack draws over: an atlas frame by key (assets.loadImageor a render texture's id), drawn opaque beneath every layer, pinned to the screen, and sampleable inside a snippet asbg/bgAt(uv).fitdefault 'cover'. Scene-scoped. Throws on an unknown key.readonly backgroundKey: string | null— The current background key, or null.add(name: string, params?: Record<string, number>, colors?: Record<string, string>): BackdropHandle— Push a backdrop by name. Params and colours override the definition's defaults. Scene-scoped. Throws if no composed pack registeredname(the message names thebackdrop-<name>pack to add), if a param or colour key is unknown, or if the definition exceeds sixteen params or four colours.define(def: BackdropDef): void— Define a backdrop this GAME owns, thenaddit by name. A pack is how a backdrop is SHARED; this is how a game writes one for itself — a title screen's own sky, an effect that exists in one scene. Defining one of the game's names again REPLACES it, live: the copies on the stack take the new definition, keeping their values by param name. An identical definition changes nothing, so a scene'sentercan define its sky every time it runs. Throws on a name a composed pack owns.clear(): void— Empty the stack. Existing handles become inert.failure(name: string): string | null— Why the backdropnamedoes not draw on this device: its compile error (WGSL on WebGPU, GLSL on WebGL2), or null. A backdrop that does not compile is reported once in the console and then skipped, never drawn; the error arrives a frame or two after it is first drawn. For tools that show a shader's errors.readonly stack: ReadonlyArray<string>— Backdrop names in draw order, bottom first; a fresh array each read.readonly available: ReadonlyArray<string>— Every backdrop a composed pack registered.
BackdropTweenOptions
interface BackdropTweenOptions
How long a tween, show or hide takes and along which curve.
duration?: number— Seconds of sim time; default 0.5. 0 is at once. Throws when negative or not finite.ease?: BackdropEase— The curve; default'easeInOutQuad'(thetweenpack's). An unknown name throws.
BackdropValues
interface BackdropValues
Params and colours to set together: handle.set({ params: { speed: 2 }, colors: { tint: '#ff0' } }).
params?: Record<string, number>— Params by name.colors?: Record<string, string>— Colours by name, as hex strings.
BackgroundFit
type BackgroundFit = 'cover' | 'contain' | 'stretch'
How background() fits the image to the screen.
CheckedBackdrop
interface CheckedBackdrop
The shape a check reads: a backdrop definition.
name: string— Its name.code: string— The WGSL snippet.params?: Record<string, number>— Its params and defaults.colors?: Record<string, string>— Its colours.


