Phaser AE

Shader backdrops

Free packv3.0.0-72D and 3DBackdrop

A stack of full-screen shader backgrounds drawn before anything else in the frame: a sky, deep space, fog, or a texture that never runs out at the edge. Each layer knows the camera and the clock, changes live, and fades in and out on its own.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Shader backdrops: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

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 name game.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. color starts transparent black; leave the premultiplied result in it. Every param is in scope BY NAME as well as by position, so a snippet reads sunSize rather than p2 — the same name the API sets. time, res, px, uv, view, bg, c0..c3 and TAU are the template's own, and a param may not take one of those names.
  • params: Record<string, number> — The params and their defaults, in p0..p15 order. At most sixteen.
  • colors: Record<string, string> — The colours and their default hex strings, in c0..c3 order. 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 if param is not one the backdrop declares, or value is 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 if param is not one the backdrop declares, or value is 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 to params over duration seconds 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 (fade 1) over duration (default 0.5 s). Resolves when shown, or when interrupted.
  • hide(opts?: BackdropTweenOptions): Promise<void> — Fade out to nothing (fade 0) over duration (default 0.5 s); a hidden backdrop is not drawn at all. hide({ duration: 0 }) right after add starts it hidden, ready to show.
  • 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's time reads now: the sim clock plus this backdrop's own offset (0 at add). 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.loadImage or a render texture's id), drawn opaque beneath every layer, pinned to the screen, and sampleable inside a snippet as bg / bgAt(uv). fit default '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 registered name (the message names the backdrop-<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, then add it 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's enter can 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 backdrop name does 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' (the tween pack'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.