Skip to content

Asset Loading

Doodle Engine loads every media file listed in the asset manifest before the game shell appears. The asset manifest is the generated list of images, audio, and video referenced by the project.

This page explains startup loading for developers changing GameShell or building a custom renderer. For adding media to game content, see Assets & Media.

The manifest separates files into two groups:

Group Includes
Shell assets Splash, loading, and title images; title music; splash and interface sounds
Game assets Location and interlude art, portraits, item and map images, game music and sound, voice, and video

AssetProvider loads the shell group first and the game group second. Its loading screen remains visible during both groups. The provider renders the game only after both groups finish, so the optional splash screen, title screen, and gameplay all begin with their referenced media available.

The screen order is:

  1. Loading screen while shell and game media load
  2. Start game button on the completed loading screen
  3. Optional splash screen
  4. Title screen
  5. Gameplay after the player starts or continues a game

The default loading screen is rendered with CSS, so it can appear before any media finishes loading. A background configured for the loading screen is included in the shell group.

When loading finishes, the player selects Start game to continue. Use GameShell’s renderLoading prop to provide custom loading content, or LoadingScreen’s renderProgress prop to replace the default progress display.

PROJECT_ID in the examples below is the stable ID exported by the generated project.ts file.

Add a shell: section to your content/game.yaml:

shell:
splash:
logo: assets/images/studio-logo.png
background: assets/images/splash-bg.jpg
sound: assets/audio/sfx/splash-sting.ogg
duration: 2000
loading:
background: assets/images/loading-bg.jpg
title:
logo: assets/images/game-logo.png
background: assets/images/title-bg.jpg
music: assets/audio/music/title-theme.ogg
showEngineTag: false # Hide "Made with Doodle Engine"
uiSounds:
click: assets/audio/ui/click.ogg
hover: assets/audio/ui/hover.ogg
menuOpen: assets/audio/ui/menu_open.ogg
menuClose: assets/audio/ui/menu_close.ogg

All fields are optional. Screens use their built-in presentation when an image or sound is omitted. Use project-relative asset paths beginning with assets/. The build checks that each referenced local file exists.

Pass a renderLoading prop to GameShell for complete control:

<GameShell
manifest={manifest}
config={config}
registry={registry}
projectId={PROJECT_ID}
renderLoading={(state) => (
<div className="my-loader">
<p>{Math.round(state.overallProgress * 100)}%</p>
<progress value={state.overallProgress} max={1} />
</div>
)}
/>

The state object includes:

{
phase: 'idle' | 'loading-shell' | 'loading-game' | 'complete' | 'error';
bytesLoaded: number;
bytesTotal: number;
assetsLoaded?: number;
assetsTotal?: number;
progress: number;
overallProgress: number;
currentAsset: string | null;
error: string | null;
}

Wrap a custom renderer in AssetProvider to use the same startup loading. Its children render after every manifest entry is ready:

import { AssetProvider } from '@doodle-engine/react';
function App() {
return (
<AssetProvider
manifest={manifest}
renderLoading={(state) => <LoadingScreen state={state} />}
>
<MyGame />
</AssetProvider>
);
}

For a release build, npm run build generates dist/sw.js. This service worker is a browser script that caches the application, content, and media for offline use after the player’s first visit.

The service worker:

  • Caches the application files, /api/content, /api/manifest, and local manifest media
  • Uses the network first for page navigation and content, with the cache available offline
  • Uses cached copies of bundles and media when available
  • Removes caches from older builds

The service worker is registered only by release builds. During development, AssetProvider still performs the same two-group startup load. The browser may retain those requests in its normal cache.

Doodle Engine’s default loader uses browser fetch and the browser Cache API. Web builds and desktop or mobile wrappers that display the web build through a local server can use it unchanged.

Pass a custom AssetLoader when the application runs in a host that retrieves or caches media differently, or when a test needs to replace network loading. The loader must provide methods for loading one or many paths, reporting availability, and clearing its cache:

import type { AssetLoader } from '@doodle-engine/core';
const localLoader: AssetLoader = {
isAvailable: async () => true,
load: async () => {},
loadMany: async (paths, onProgress) => {
paths.forEach((path, index) =>
onProgress?.(index + 1, paths.length, path)
);
},
clear: async () => {},
};
<GameShell
registry={registry}
config={config}
manifest={manifest}
projectId={PROJECT_ID}
assetLoader={localLoader}
/>