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.silentas forload: 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 anAssetsFile. Returns at once with aLoadJobto poll (progress,current,failed) or await. Every item is retried with a doubling delay, then listed infailed; 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, anAssetsFileitem with an unknown kind or no url, or an unknownpack; an image whose key is already loaded 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 infailedWARNS 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 thatloadandloadImagefetch, so a list names files rather than repeating a long address: with'https://cdn.example.com/game/',title.webpfetcheshttps://cdn.example.com/game/title.webp. A url that is already absolute is fetched as written:https:,http:,data:orblob:,//host/…, or a root path/…(the page's own server). Default'': every url as written. A job'sbaseURLoption, then the file's, wins over this.get<T = unknown>(key: string): T— What a load put underkey, typed by the caller. Throws on an unknown key (the message lists what is loaded).has(key: string): boolean— Hasloadput anything underkey? (A picture made withpaint,pixelsoraddStripis a strip: seestrip.)failure(key: string): string | null— Whykeyis not here: the error from the load that failed, ornullif 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:countframes ofw×hlaid out left to right insrc(default: as many as its width holds). Returns the first frame index. Throws if the key is taken or the atlas is full. A nullsrcreserves blank frames. A big picture belongs inloadinstead, which can give it a texture of its own.addGrid(key: string, src: CanvasImageSource | null, w: number, h: number, cols: number, rows: number): number— Register a GRID sheet in the atlas:cols×rowsframes ofw×h, row-major. Returns the first frame index. Throws asaddStripdoes.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 withtexture: '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 atrespixels 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 anddrawnever runs. Throws for a size that is not above 0 or a key already taken.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 itsres. 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 frompalette, any other character (.by habit) is clear, and each pixel isscaleunits 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 ofw×hframes (a tileset, a sprite sheet from an asset generator), into the atlas or a texture of its own by the same rule as an imageLoadItem. 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 fromcols/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 fromcanvas, uploaded again after eachredrawof its frame. What a full-screen render texture is made of. Returns its frame index.isPicture(w: number, h: number): boolean— Is one frame ofw×ha picture (a texture of its own) rather than an atlas frame: past 1024 px on a side, or over a quarter of the atlas?redraw(frame: number, src: CanvasImageSource): void— Repaint one frame from a canvas (a render texture after a draw): an atlas frame's region, or a live canvas fromaddCanvas. 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, asstripdoes.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 needsfetch. Prefer ajsonitem inload, which has all of those.readonly loaded: boolean— True when noload,loadImage,jsonor 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 relativeurl(seeAssets.baseURL). Default:assets.baseURL.readonly spriteSize?: number— The copy everyspriteitem loads unless it says its ownsize: 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 whatever its size, and a * picture that does not fit is then the atlas-full error. */ | { 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, overtotal. 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 afterabort().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: readokandfailedoff 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, soawait assets.load([...])waits for it. It has to be: that line is what everyone writes, it reads as correct, and withoutthenit resolves instantly on a plain object — so the game carries on with an empty atlas and dies on the firstframe()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,finishedbecomes 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 withfailedlisted.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,failedand the hooks work either way, for a game's own indicator.preloader.runandpreloader.showput a screen up on purpose whatever this says.baseURL?: string— Put in front of every relativeurlin this job. Default: the file'sbaseURL, elseassets.baseURL.spriteSize?: number— The copy aspriteitem without its ownsizeloads. Default: the file'sspriteSize, else 256.pack?: string— Load one named pack of anAssetsFile. 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 alwaysawait 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: stringreadonly key: stringreadonly url?: stringreadonly file?: stringreadonly size?: numberreadonly [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.
