Preloading
The behaviour described on this page is available since 0.17.0.
The player fetches and decodes a scene's assets before that scene paints. Since 0.17.0 that work starts as soon as the story is loaded, rather than when the game is entered, and it runs in two tiers, so the first frame waits only on the assets it needs.
onPreloadComplete moved. It now fires before the game is entered, while a menu may still be on screen. For the "the game has content on screen" signal, use onFirstSceneReady. See Which signal to wait on.
When preloading starts
Player registers story.entryScene as the preloading scene as soon as the story is loaded, so the assets are fetched behind whatever is already on screen. Nothing is mounted and no action runs.
The automatic registration applies only when there is neither a preloading scene nor a mounted scene, so a host that calls preloadScene itself keeps its own choice.
Critical and look-ahead tiers
A scene's asset list is transitive: it carries the asset set of every scene reachable from it. The pass runs in two tiers so the first frame does not wait for all of them.
| Tier | What it holds | How it runs |
|---|---|---|
| Critical | What the scene about to paint registers directly: its own backgrounds and images, plus the immediate background of any scene it jumps to | Unpaced, and the only tier that gates event:preloaded.complete, and therefore the first painted frame |
| Look-ahead | The full asset set of every scene reachable from here, minus anything already in the critical tier | Runs after the critical tier, paced by preloadDelay. Nothing waits for it |
preloadDelay does not charge the first frame: it paces speculative work only. The cache-eviction pass runs once over the union of both tiers, and is skipped for a superseded pass, so switching scenes mid-preload keeps the images the current scene just cached.
Games running with preloadAllImages: false keep the predict-by-action behaviour, and none of this applies to them.
Decoded bitmaps
The critical tier holds its decoded elements until the source leaves the cache, so the first visible frame does not decode again.
The look-ahead tier does not hold them. A full-resolution bitmap costs width × height × 4 bytes.
Sound preloading
The critical tier also warms the current scene's sounds, through preload on the audio manager:
<Player
story={story}
onReady={({ gameState }) => {
// fetch and decode a source into the audio cache without playing it
void gameState.audioManager.preload(bgm);
}}
/>preload(sound: Sound): Promise<void>- Resolves when the source is in the audio cache. A source that fails to load is logged as a warning and loads on first play instead, so the promise never rejects.
Never gate anything on this promise. The audio context stays locked until the browser's autoplay policy is satisfied by a user gesture, so an audio warm-up can sit pending on a page nobody has touched yet. The engine's own call is fire-and-forget.
Only the current scene's sounds are warmed automatically. A look-ahead scene's audio is left to that scene's own pass.
Which signal to wait on
| Signal | Fires when |
|---|---|
| onReady | The Player instance is initialized. This is where newGame() belongs. It is not a preload or a first-render guarantee. |
onPreloadComplete | The initial preload pass (the critical tier) has finished. Since 0.17.0 this is before the game is entered: a menu may still be on screen and no scene is mounted. |
onFirstSceneReady | The preload pass has finished, the first scene component has mounted, and the browser has had a frame to render it. This is the "there is content on screen" signal. |
The latter two exist as a Player prop and as a Game method, and each fires at most once per game lifecycle:
<Player
story={story}
onReady={({ liveGame }) => liveGame.newGame()}
onPreloadComplete={() => setLoadingBarDone(true)} // menu may still be up
onFirstSceneReady={() => setLoadingOverlay(false)} // the scene is painted
/>// Same points, for code that is not the Player element.
await game.whenPreloadComplete();
await game.whenFirstSceneReady();
game.isPreloadComplete();
game.isFirstSceneReady();See Game for the full listener set: on*, once*, when* and is* for both points.
Upgrading from 0.16 and earlier
This is a behavioural break, not a new API. onPreloadComplete, oncePreloadComplete, whenPreloadComplete() and event:preloaded.complete all fire before the game is entered, rather than after newGame() has mounted a scene. The names and signatures did not change, so nothing fails to compile, and a host that hid its loading screen there now uncovers an unmounted stage.
- Used to gate a loading step (a progress bar, a "Start" button that should not be pressable while assets are still coming down): no change needed.
- Used to mean "the game has content on screen": switch to
onFirstSceneReady/whenFirstSceneReady(). Those are unchanged and still require a mounted scene.
The ctx.scene a preloadComplete listener receives may now be the preloading scene rather than a mounted one.
onPreloadedReady is deprecated in favour of onPreloadComplete.
Preloading a scene yourself
To warm a scene other than the entry scene, name it. A save-slot screen that knows which scene the player is about to resume into can call:
gameState.preloadScene(scene); // or a Story, which uses its entry sceneCalling this before the Player registers the entry scene prevents the automatic registration.
Configuration
These live on GameConfig:
preloadAllImages(defaulttrue) - Run the two-tier scene pass. Withfalse, the player predicts the nextmaxPreloadActionsactions and preloads their sources instead.preloadConcurrency(default5) - How many images are fetched at once, in both tiers.preloadDelay(default100) - Pacing between look-ahead batches. The critical tier is not paced.waitForPreload(defaulttrue) - Hold the player's reveal until the critical tier is done.forceClearCache(defaultfalse) - Clear the image cache when the scene changes.maxPreloadActions(default10) - How many actions ahead thepreloadAllImages: falsepath predicts.