← The API

Phaser AE

API docs

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

ContentsAssets

Need to get started? Install the Phaser AE CLI →

Content and maths

Assets

The loader, asset packs (assets.json), the atlas, pictures in textures of their own, paint, pixels

engine/kernel/src/assets.ts

Types

AssetActivity

type AssetActivity = Pick<LoadJob, 'progress' | 'loaded' | 'total' | 'current' | 'failed' | 'finished' | 'aborted' | 'done'> & { retry?(): LoadJob | null }

From @phaser-ae/kernel

Observable loading work, including model and audio decoding outside the file queue. A file job can be retried; tracked work cannot.

Assets

interface Assets

From @phaser-ae/kernel

The atlas and the loader: strips, grids, image files, fetched JSON, and a queue of anything. One per game.

  • track<T>(key: string, work: () => Promise<T>, opts?: Pick<LoadOptions, 'silent'>): Promise<T> — Track asynchronous asset preparation through completion, including decoding. Preserves the result or rejection of work. silent as for load: by default only work started while preloading raises the loading screen.
  • readonly preloading: boolean — True until the first scene enters: the PRELOAD, when everything the game will need is meant to load, under the loading screen. A load started after it is silent unless it asks otherwise (LoadOptions.silent).
  • load(items: ReadonlyArray<LoadItem> | AssetsFile, opts?: LoadOptions): LoadJob — Load a STACK of things: images (sprites into the atlas, big pictures into textures of their own), sprites, JSON, text, XML, bytes, audio, video and fonts, from a list or an AssetsFile. Returns at once with a LoadJob to poll (progress, current, failed) or await. Every item is retried with a doubling delay, then listed in failed; the job itself never rejects. A URL already fetched as the same kind is not fetched again. Throws at once, before loading anything, for a list that names a key twice, an AssetsFile item with an unknown kind or no url, or an unknown pack. An image loaded again under its key from the same url (a scene that loads its art each time it enters) lands at once with the frames it has; another url or a drawing under a key in use replaces that picture, in place when it is the same shape, so the frame indices a game holds stay good. A key already loaded as another kind fails as an item. Hooks: assets.loadStart, assets.itemLoaded, assets.itemFailed, assets.prepare, assets.loadDone, assets.activity. Headless (no document) nothing is fetched and every item resolves blank, so a replay runs the same. A job that finishes with anything in failed WARNS on the console, whether or not the caller looked. A silent failure is worse than a noisy one: it surfaces frames later as "no strip x" from somewhere else entirely, which reads as a typo rather than as the load that never landed.
  • baseURL: string — Put in front of every RELATIVE url that load and loadImage fetch, so a list names files rather than repeating a long address: with 'https://cdn.example.com/game/', title.webp fetches https://cdn.example.com/game/title.webp. A url that is already absolute is fetched as written: https:, http:, data: or blob:, //host/…, or a root path /… (the page's own server). Default '': every url as written. A job's baseURL option, then the file's, wins over this.
  • get<T = unknown>(key: string): T — What a load put under key, typed by the caller. Throws on an unknown key (the message lists what is loaded).
  • has(key: string): boolean — Has load put anything under key? (A picture made with paint, pixels or addStrip is a strip: see strip.)
  • failure(key: string): string | null — Why key is not here: the error from the load that failed, or null if nothing was ever asked for under that key. A missing key and a key that FAILED are different problems and only one of them is a typo, so every "no such key" message the kernel throws checks this first and says which it is.
  • readonly keys: ReadonlyArray<{ key: string; kind: LoadItem['kind'] }> — Every loaded key with its kind, in load order.
  • addStrip(key: string, src: CanvasImageSource | null, w: number, h: number, count?: number): number — Register a sprite sheet IN THE ATLAS: count frames of w×h laid out left to right in src (default: as many as its width holds). Returns the first frame index. The frames go on the first atlas page with room for all of them, and a new page opens when none has, so this never fails for want of room; a sheet bigger than an empty page gets a texture of its own. A null src reserves blank frames. The same key again with the same frame size and count repaints those frames in place and returns the same index (what a scene that adds its art every time it enters needs); another shape takes new frames, and the key moves to them. A big picture belongs in load.
  • addStrips(items: ReadonlyArray<{ key: string; src: CanvasImageSource | null; w: number; h: number; count?: number }>): number[] — Several strips that are DRAWN TOGETHER, such as a model's colour, normal and roughness maps, added as addStrip adds each, but onto ONE atlas page when one can hold them all, so a pass that binds one page per draw sees every map. Returns each strip's first frame index, in order. A set too big for one page goes in strip by strip.
  • addGrid(key: string, src: CanvasImageSource | null, w: number, h: number, cols: number, rows: number): number — Register a GRID sheet in the atlas: cols×rows frames of w×h, row-major. Returns the first frame index. Placed, and replaced, as addStrip is.
  • paint(key: string, w: number, h: number, draw: (ctx: PaintContext, frame: number) => void, opts?: { frames?: number; res?: number }): number — A picture by name, painted with the ordinary 2D canvas: paint('cake', 86, 46, ctx => { … }). With { frames: 4 } it is four pictures of one size side by side, draw(ctx, i) once for each, each on its own w×h clipped to it: an animation, or a picture's states. A sprite shows it with texture: 'cake', at its size. A big one (a background) gets a texture of its own, as a big loaded image does. Draw in the picture's own units: it is drawn at res pixels a unit (default 2, sharp on a high-density screen; less if that would pass 4096 pixels) and shows at w×h. Returns the first frame index. Headless the frames are registered blank and draw never runs. The same name painted again at the same size and frame count repaints it in place and returns the same index; another size takes new frames. Throws for a size that is not above 0, or for the name "white".
  • sizeOf(index: number): { w: number; h: number } | null — A frame's size in units, as a sprite showing it is sized: a painted one's pixels divided by its res. Null for an unknown frame.
  • pixels(key: string, rows: ReadonlyArray<string>, palette: Readonly<Record<string, string>>, scale?: number): number — Pixel art by name, from rows of characters: each character is a colour from palette, any other character (. by habit) is clear, and each pixel is scale units square (default 1). Returns the first frame index.
  • loadImage(key: string, url: string, w: number, h: number, opts: { cols: number; rows: number }): Promise<{ first: number; count: number }> — Load an image file as a grid of w×h frames (a tileset, a sprite sheet from an asset generator), into the atlas or a texture of its own by the same rule as an image LoadItem. Resolves to the first frame index and the count. In a browser a missing or too-small image rejects. Headless (no image decoding) the frames are registered blank from cols/rows, which is why they are asked for: a replay must not depend on a fetch.
  • addCanvas(key: string, canvas: OffscreenCanvas | null, w: number, h: number): number — A picture that changes, too big for the atlas: a texture of its own drawn from canvas, uploaded again after each redraw of its frame. What a full-screen render texture is made of. Returns its frame index.
  • isPicture(w: number, h: number): boolean — Is one frame of w×h a picture (a texture of its own) rather than an atlas frame: past 1024 px on a side, or over a quarter of an atlas page?
  • redraw(frame: number, src: CanvasImageSource): void — Repaint one frame from a canvas (a render texture after a draw): an atlas frame's region on whichever page it is, or a live canvas from addCanvas. Backends upload only that. A loaded picture in a texture of its own throws.
  • strip(key: string): { first: number; count: number } — A picture's first frame and frame count, by key: anything loaded as an image or sprite, painted, or added. Throws for an unknown key, saying whether it failed to load.
  • frames(key: string): number — A picture's first frame index; frames('crab') → 12. Throws for an unknown key, as strip does.
  • frame(key: string, offset?: number): FrameRef — One frame of a picture by key and offset (default 0), with its size in texture pixels. Throws on an unknown key; an offset past the picture is not checked.
  • json<T>(path: string): Promise<T> — Fetch and parse one JSON file now, as tracked loading work: no base URL, no retries, no cache, and it needs fetch. Prefer a json item in load, which has all of those.
  • readonly loaded: boolean — True when no load, loadImage, json or tracked preparation is in flight. Starts true; goes false while any fetch is pending.
  • readonly progress: number — How far the loading in flight has got, 0..1: items landed or failed for good, over every item asked for since the loader was last idle. 1 when nothing is loading. What a host shows while the game is not ready yet.

AssetsFile

interface AssetsFile

From @phaser-ae/kernel

The game's files as a FILE holds them: assets.json, written by a pipeline or by hand and imported whole (import assets from './assets.json'). baseURL goes in front of every relative url, so the file names files (title.webp) instead of repeating a long address on every line.

The files sit in named PACKS, each loaded when the game needs it: load(assets, { pack: 'title' }) for what the title screen shows, under the loading screen, then load(assets, { pack: 'game' }) quietly while the title is up. A game names and splits its packs as it likes (level-1, level-2 …). items is the same list without packs. load(assets) with no pack loads every one.

The items are LoadItems as JSON holds them: a kind is a plain string there, so each is checked as it loads, and an unknown kind or a missing url throws naming the item. A file cannot hold a drawing; drawn art goes in a list.

  • readonly baseURL?: string — Put in front of every relative url (see Assets.baseURL). Default: assets.baseURL.
  • readonly spriteSize?: number — The copy every sprite item loads unless it says its own size: change this one number to load them all at 128. Default 256.
  • readonly packs?: Readonly<Record<string, ReadonlyArray<PackItem>>> — The files in named packs, each loaded when the game asks for it by name.
  • readonly items?: ReadonlyArray<PackItem> — The files without packs, loaded together.

FrameRef

interface FrameRef

From @phaser-ae/kernel

One frame, as frame() returns it: an atlas frame, or a frame of a picture in a texture of its own.

  • readonly index: number — Global frame index, the number a sprite column holds.
  • readonly width: number — Frame width in texture pixels.
  • readonly height: number — Frame height in texture pixels.

LoadItem

type LoadItem = /** * An image as `cols`×`rows` frames of `w`×`h` (default: one frame, the whole image). `get(key)` → `{ first, count }`. * * Sprites and sheets go in the ATLAS. A big PICTURE — one frame wider or taller than 1024 px, or one that would take * more than a quarter of the atlas — goes in a texture of its own, uploaded once and never scaled: a title screen * or a background is drawn alone and gains nothing from sharing, and packed it fills the atlas the sprites need. * A sheet stays in the atlas unless it does not fit, and then the whole sheet gets one texture of its own. * Either way the key, `frame(key)` and the frame indices are the same, and a sprite draws it with `frame` in any * layer. `atlas: false` gives any image a texture of its own; `atlas: true` packs it on an atlas page whatever its * size, unless it is bigger than a page, which still gets a texture of its own. */ | { kind: 'image'; key: string; url: string; w?: number; h?: number; cols?: number; rows?: number; atlas?: boolean } /** * ART THE GAME DREW ITSELF — into the atlas exactly as a fetched image goes in, `cols`/`rows` and all. * There is no file, so there is nothing to ship, nothing to cache-bust, and nothing to get out of step * with the code that reads it. * * `draw` is a FUNCTION rather than a canvas on purpose: it is called only where there is a canvas to * draw on. Headless — a replay test, a server tick — the frames are still reserved and every index the * game holds is still valid, and the drawing code never runs. Hand over a canvas instead and the demo * dies on `document` before the first frame. A big drawing gets a texture of its own by the same rule, and `atlas` overrides it the same way. */ | { kind: 'image'; key: string; draw: () => CanvasImageSource | ImageData; w?: number; h?: number; cols?: number; rows?: number; atlas?: boolean } /** * A SPRITE copied at several sizes, each a file `<file>-<size>.webp` (`pip-128.webp`, `pip-256.webp`). `file` is * the name without the size and `size` picks the copy to fetch: by default `spriteSize` (the job's, else the * file's, else 256). It loads exactly as an image does, `frames(key)` and all. The size is only which file is * fetched, never how big it is drawn: pick the smallest that stays sharp at the size the game draws it. */ | { kind: 'sprite'; key: string; file: string; size?: number; atlas?: boolean } /** Parsed JSON. `get<T>(key)` → T. */ | { kind: 'json'; key: string; url: string } /** A string. */ | { kind: 'text'; key: string; url: string } /** Parsed XML (a `Document`); headless, the raw string. */ | { kind: 'xml'; key: string; url: string } /** Raw bytes, an `ArrayBuffer` (a GLB, a wasm, a save). */ | { kind: 'binary'; key: string; url: string } /** * An audio file's bytes. The audio pack DECODES it inside the job, so a sample is ready to play the moment * the job finishes (`sound.sample(key)`). `stream: true` is for music and long ambience: kept as the file * and streamed by `sound.music(key)`, never decoded into the megabytes a long track takes as samples. */ | { kind: 'audio'; key: string; url: string; stream?: boolean } /** A video element, ready to play (`get(key)` → `HTMLVideoElement`); headless, null. */ | { kind: 'video'; key: string; url: string } /** A web font: `family` registered with `document.fonts` (a render texture can draw with it); headless, a no-op. */ | { kind: 'font'; key: string; url: string; family: string }

From @phaser-ae/kernel

One thing to load, by kind. key is how the game asks for it afterwards (unique per game); url is fetched.

LoadJob

interface LoadJob

From @phaser-ae/kernel

A load in progress, or finished. Poll it each step for a bar, or await done.

  • readonly progress: number — Items landed or failed for good, over total. 0..1.
  • readonly loaded: number — Items landed.
  • readonly total: number — Items in the job.
  • readonly current: string | null — The key being fetched now (the first in flight), or null.
  • readonly failed: ReadonlyArray<{ key: string; url: string; error: string }> — Items that failed after every retry; empty when all is well.
  • readonly finished: boolean — Everything has landed or failed for good (or the job was aborted).
  • readonly ok: boolean — Finished with nothing failed and not aborted.
  • readonly aborted: boolean — True after abort().
  • readonly elapsed: number — Seconds since the job started, on the wall clock.
  • readonly done: Promise<void> — Resolves when the job finishes — never rejects, and never resolves WITH the job: read ok and failed off the job itself. It cannot carry the job, because the job is thenable, and a promise that resolves with a thenable is awaited again — a job that resolved with itself would never settle.
  • then<R1 = void, R2 = never>(ok?: (() => R1 | PromiseLike<R1>) | null, err?: ((e: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2> — A job is AWAITABLE, so await assets.load([...]) waits for it. It has to be: that line is what everyone writes, it reads as correct, and without then it resolves instantly on a plain object — so the game carries on with an empty atlas and dies on the first frame() call, pointing at the frame rather than at the wait that never happened.
  • abort(): void — Stop: in-flight fetches are cancelled, pending items are not started, finished becomes true.
  • retry(): LoadJob | null — Try what failed again: a new job for the failed items, with this job's options, or null when nothing failed. A pack's job is replaced by the new one, so a scene that asks for the pack from then on waits for the retry. The loading screen's Retry button calls this.

LoadOptions

interface LoadOptions

From @phaser-ae/kernel

How a load behaves.

  • retries?: number — Times to retry a failed item before giving up. Default 2.
  • retryDelay?: number — Seconds to wait before a retry; doubles each time. Default 0.5.
  • concurrency?: number — Fetches in flight at once. Default 6.
  • abortOnFail?: boolean — Give up on the whole job at the first item that fails for good (aborting the rest). Default false: the job finishes with failed listed.
  • silent?: boolean — Load in the background, with no automatic loading screen. Default: false while the game is PRELOADING (before its first scene enters), true after, so the loading screen belongs to the boot and never interrupts play. Progress, done, failed and the hooks work either way, for a game's own indicator. preloader.run and preloader.show put a screen up on purpose whatever this says.
  • baseURL?: string — Put in front of every relative url in this job. Default: the file's baseURL, else assets.baseURL.
  • spriteSize?: number — The copy a sprite item without its own size loads. Default: the file's spriteSize, else 256.
  • pack?: string — Load one named pack of an AssetsFile. A pack loads once: asking for it again, while it loads or after, gives the same job, so a scene that needs the pack can always await game.assets.load(assets, { pack: 'game' }).

PackItem

type PackItem = { readonly kind: string; readonly key: string; readonly url?: string; readonly file?: string; readonly size?: number; readonly [option: string]: unknown }

From @phaser-ae/kernel

One item of an AssetsFile: a LoadItem as JSON types it, its kind a string and its options whatever the kind takes. Every kind has a url but a sprite, which has a file and maybe a size.

  • readonly kind: string
  • readonly key: string
  • readonly url?: string
  • readonly file?: string
  • readonly size?: number
  • readonly [option: string]: unknown

PaintContext

type PaintContext = CanvasRenderingContext2D & OffscreenCanvasRenderingContext2D

From @phaser-ae/kernel

The 2D context paint draws with. It is an OffscreenCanvas's, and typed as both kinds, so a drawing helper written for CanvasRenderingContext2D or for OffscreenCanvasRenderingContext2D takes it.