Loader is the asset loading entrypoint. It uses a class-token dispatch system
that mirrors the RendererRegistry pattern — each asset class has a registered
factory, and the loader resolves the correct factory via prototype-chain walking.
- load assets by class token (e.g.
Texture,Sound,Json) - delegate decoding/creation to typed
AssetFactoryimplementations - manage an internal resource store keyed by class + alias
- optionally use
CacheStoreimplementations for persistence (e.g. IndexedDB) - background loading with concurrency throttling and priority boost
// Single asset — returns the loaded resource directly
const hero = await loader.load(Texture, 'hero.png');
// Multiple assets with explicit aliases
const textures = await loader.load(Texture, { hero: 'hero.png', bg: 'bg.png' });
// Generic JSON with type assertion
const config = await loader.load<GameSettings>(Json, 'settings.json');
// Background loading
loader.add(Texture, { hero: 'hero.png', enemy: 'enemy.png' });
loader.backgroundLoad();
const hero = await loader.load(Texture, 'hero'); // priority boost
// Retrieval after loading
const tex = loader.get(Texture, 'hero'); // throws if missing
const maybe = loader.peek(Texture, 'hero'); // null if missing
// Cleanup
loader.unload(Texture, 'hero');
loader.unloadAll();import { Loader, Texture, Sound, defineAssetManifest } from 'exojs';
const manifest = defineAssetManifest({
bundles: {
boot: [
{ type: Texture, alias: 'logo', path: 'ui/logo.png' },
{ type: Sound, alias: 'click', path: 'audio/click.wav' },
],
shared: [
{ type: Texture, alias: 'atlas', path: 'textures/atlas.png' },
],
gameplay: [
{ type: Texture, alias: 'player', path: 'sprites/player.png' },
{ type: Texture, alias: 'atlas', path: 'textures/atlas.png' }, // shared alias/path is allowed
],
},
});
const loader = new Loader({ resourcePath: '/assets/' });
loader.registerManifest(manifest);
await loader.loadBundle('boot', {
onProgress(loaded, total) {
console.log(`boot bundle: ${loaded}/${total}`);
},
});
await loader.loadBundle('shared', { background: true });
await loader.loadBundle('gameplay');
const logo = loader.get(Texture, 'logo');Bundle notes:
loadBundle(name)loads only that bundle and returnsPromise<void>- assets are still retrieved with
get()/peek()/has()after loading loader.onBundleProgressemits(bundleName, loaded, total)for bundle-level progresshasBundle(name)returnstrueonly when every entry in the bundle is cached in memory- unknown bundles throw from
loadBundle()and returnfalsefromhasBundle()
const scene = Scene.create({
async load(loader) {
await loader.load(Texture, { hero: 'hero.png' });
await loader.load(Sound, { jump: 'jump.wav' });
},
init(loader) {
this._sprite = new Sprite(loader.get(Texture, 'hero'));
},
unload(loader) {
loader.unloadAll();
},
});AssetFactory<T>— factory interface (process + create)AssetConstructor<T>— class reference used as registry keyFactoryRegistry— maps class tokens to factories (prototype-chain walking)CacheStore— persistent storage interface (e.g.IndexedDbStore)LoaderOptions— configuration (resourcePath, requestOptions, cache, concurrency)
Built-in class tokens for loading:
| Token | Returns | Factory |
|---|---|---|
Texture |
Texture |
TextureFactory |
Sound |
Sound |
SoundFactory |
Music |
Music |
MusicFactory |
Video |
Video |
VideoFactory |
FontFace |
FontFace |
FontFactory |
HTMLImageElement |
HTMLImageElement |
ImageFactory |
Json |
unknown (narrow via generic) |
JsonFactory |
TextAsset |
string |
TextFactory |
SvgAsset |
HTMLImageElement |
SvgFactory |
VttAsset |
Array<VTTCue> |
VttFactory |
ArrayBuffer |
ArrayBuffer |
BinaryFactory |
WebAssembly.Module |
WebAssembly.Module |
WasmFactory |
Custom types: loader.register(MyClass, new MyFactory()) — same call shape as built-ins.
register(type, factory)— register a factory for a class tokenadd(type, path | paths | items)— register aliases without loadingregisterManifest(manifest)— register bundle definitions without loadingload(type, path | paths | items, options?)— load and return resourcesloadBundle(name, options?)— load a named manifest bundle (Promise<void>)backgroundLoad()— start loading all registered items concurrentlyloadAll()— await all registered itemsget(type, alias)/peek(type, alias)/has(type, alias)— retrievalhasBundle(name)— check if every bundle entry is currently loadedunload(type, alias)/unloadAll(type?)— cleanupdestroy()— tear down loader and all factories
- resource path prefixing is controlled by
resourcePath - persistence is opt-in via the
cacheoption onLoaderOptions IndexedDbStorewraps IndexedDB as aCacheStore- background loads respect
concurrency(default 6); directload()calls bypass the limit