Asset Loading
Estella provides the Assets resource (an AssetServer instance) for loading textures, Spine animations, materials, and generic files. Access it in any system via Res(Assets).
Accessing Assets
import { defineSystem, addStartupSystem, Res } from 'esengine';import { Assets } from 'esengine';
addStartupSystem(defineSystem( [Res(Assets)], async (assets) => { const tex = await assets.loadTexture('assets/player.png'); console.log(`Loaded ${tex.width}x${tex.height}, handle: ${tex.handle}`); }));Texture Loading
loadTexture(path)
Loads an image and returns a TextureInfo object. The image is automatically flipped vertically for OpenGL UV conventions.
const tex = await assets.loadTexture('assets/player.png');sprite.texture = tex.handle;Returns: TextureInfo { handle: TextureHandle, width: number, height: number }
Other Texture Methods
| Method | Returns | Description |
|---|---|---|
loadTextureRaw(source) | Promise<TextureInfo> | Load without vertical flip (used by Spine runtime) |
getTexture(path) | TextureInfo | undefined | Returns cached TextureInfo if already loaded |
hasTexture(path) | boolean | Returns true if the texture is cached |
releaseTexture(path) | void | Decrements ref count; releases from GPU when count reaches zero |
releaseAll() | void | Releases all cached assets (textures, fonts, materials, shaders, prefabs, spine) |
9-Slice Metadata
For 9-slice sprites, set border metadata after loading:
const tex = await assets.loadTexture('assets/panel.png');assets.setTextureMetadata(tex.handle, { left: 10, right: 10, top: 10, bottom: 10});You can also set metadata by path if the texture is already loaded:
assets.setTextureMetadataByPath('assets/panel.png', { left: 10, right: 10, top: 10, bottom: 10});Spine Loading
const result = await assets.loadSpine('assets/hero.json', 'assets/hero.atlas');if (!result.success) { console.error(result.error);}loadSpine automatically:
- Fetches the atlas file and writes it to the virtual filesystem
- Parses the atlas for texture filenames and loads each texture
- Fetches the skeleton file (
.jsonor.skelbinary) and writes it to the virtual filesystem
| Method | Returns | Description |
|---|---|---|
loadSpine(skeleton, atlas, baseUrl?) | Promise<SpineLoadResult> | Load Spine skeleton and atlas |
isSpineLoaded(skeleton, atlas) | boolean | Check if a Spine asset pair is already loaded |
releaseSpine(key) | void | Release a loaded Spine asset (key = "skeleton:atlas") |
BitmapFont Loading
const fontHandle = await assets.loadBitmapFont('assets/my-font.fnt');Supports .fnt (BMFont text format) and .bmfont (JSON metadata) files. The loader automatically resolves and loads referenced texture atlases.
| Method | Returns | Description |
|---|---|---|
loadBitmapFont(path, baseUrl?) | Promise<FontHandle> | Load a bitmap font file and its textures. Returns a font handle |
getFont(path) | FontHandle | undefined | Get cached font handle, undefined if not loaded |
releaseFont(path) | void | Release the font and its textures from memory |
Prefab Loading
const prefab = await assets.loadPrefab('prefabs/Enemy.esprefab');Loads and parses a .esprefab file. The result is a PrefabData object that can be passed to instantiatePrefab().
| Method | Returns | Description |
|---|---|---|
loadPrefab(path, baseUrl?) | Promise<PrefabData> | Load and cache a prefab file |
See Prefabs for the full prefab API.
Generic File Loading
| Method | Returns | Description |
|---|---|---|
loadJson<T>(path, options?) | Promise<T> | Load and parse a JSON file |
loadText(path, options?) | Promise<string> | Load a text file |
loadBinary(path, options?) | Promise<ArrayBuffer> | Load a binary file |
const config = await assets.loadJson<GameConfig>('assets/config.json');const csv = await assets.loadText('assets/levels.csv');const data = await assets.loadBinary('assets/tilemap.bin');FileLoadOptions
| Option | Type | Description |
|---|---|---|
baseUrl | string | Override the base URL for this request |
noCache | boolean | Skip cache — always fetch from network |
Batch Loading
Load all assets from an AddressableManifest in parallel with loadAll:
const manifest = await assets.loadJson('assets/addressable-manifest.json');assets.setAddressableManifest(manifest);
const bundle = await assets.loadAll(manifest);
// Access loaded assets from the bundleconst bgTex = bundle.textures.get('bg');const config = bundle.json.get('game-config');You can also load a subset of assets by label or group:
const bundle = await assets.loadByLabel('ui-sprites');const levelBundle = await assets.loadGroup('level-1');AssetBundle
The returned AssetBundle contains Maps keyed by address (or path if no address is set):
| Field | Type |
|---|---|
textures | Map<string, TextureInfo> |
materials | Map<string, LoadedMaterial> |
spine | Map<string, SpineLoadResult> |
fonts | Map<string, FontHandle> |
prefabs | Map<string, PrefabData> |
json | Map<string, unknown> |
text | Map<string, string> |
binary | Map<string, ArrayBuffer> |
Material Loading
const loaded = await assets.loadMaterial('assets/effects/glow.esmaterial');sprite.material = loaded.handle;| Method | Returns | Description |
|---|---|---|
loadMaterial(path, baseUrl?) | Promise<LoadedMaterial> | Load and compile a .esmaterial file |
getMaterial(path, baseUrl?) | LoadedMaterial | undefined | Get cached material if already loaded |
hasMaterial(path, baseUrl?) | boolean | Check if a material is cached |
See Materials & Shaders for the full material API and .esmaterial file format.
Caching
All load methods cache by path. Loading the same path twice returns the cached result without a network request. Use the noCache option on generic file loaders to bypass caching:
const fresh = await assets.loadJson('assets/config.json', { noCache: true });Base URL
Set assets.baseUrl to prefix all relative paths:
assets.baseUrl = 'https://cdn.example.com/game';const tex = await assets.loadTexture('sprites/player.png');// fetches https://cdn.example.com/game/sprites/player.pngAbsolute paths and full URLs are not affected by baseUrl.
Scene Loading
Load a scene file with all its referenced assets in a single call:
const sceneData = await assets.loadJson<SceneData>('assets/scenes/main.esscene');const entityMap = await assets.loadScene(world, sceneData);loadScene pre-collects all asset references in the scene (textures, materials, Spine, fonts, prefabs), loads them in parallel via Promise.all, then instantiates the entity hierarchy. Returns a Map<number, Entity> mapping scene IDs to runtime entities.
Embedded Assets
For Playable Ad builds, the editor’s build pipeline automatically embeds all referenced assets as data URIs into the output bundle. At runtime, assets.loadTexture() / assets.loadJson() and other load calls resolve from the embedded data instead of fetching over the network — no code changes are needed.
For custom builds, you can register embedded assets manually:
assets.registerEmbeddedAssets({ 'assets/player.png': 'data:image/png;base64,...', 'assets/config.json': 'data:application/json;base64,...',});Addressable Assets
Addressable assets let you load resources by logical address, label, or group instead of raw file paths. The editor builds an AddressableManifest during the build process, which maps addresses and labels to asset paths.
Loading by Address
const texture = await assets.load('player-idle');To resolve an address to its manifest entry without loading:
const entry = assets.resolveAddress('player-idle');if (entry) { console.log(`Type: ${entry.type}, Path: ${entry.path}`);}Loading by Label
Load all assets tagged with a label. Returns an AssetBundle:
const bundle = await assets.loadByLabel('ui-sprites');for (const [path, tex] of bundle.textures) { console.log(`Loaded ${path}: ${tex.width}x${tex.height}`);}Loading by Group
Load all assets in a named group:
const bundle = await assets.loadGroup('level-1');Setting the Manifest
The manifest is generated by the editor during build. At runtime, set it before loading:
import { Assets } from 'esengine';
const manifest = await assets.loadJson('assets/addressable-manifest.json');assets.setAddressableManifest(manifest);Manifest Format
The AddressableManifest has the following structure:
interface AddressableManifest { version: '2.0'; groups: Record<string, AddressableManifestGroup>;}
interface AddressableManifestGroup { bundleMode: string; labels: string[]; assets: Record<string, AddressableManifestAsset>;}
interface AddressableManifestAsset { path: string; address?: string; type: AddressableAssetType; size: number; labels: string[]; metadata?: { atlas?: string; atlasPage?: number; atlasFrame?: { x: number; y: number; width: number; height: number }; };}Each group contains assets with their paths, optional addresses, labels, and type-specific metadata. Asset types include texture, material, spine, bitmap-font, prefab, json, text, binary, and audio.
Next Steps
- Materials & Shaders — custom shaders and blend modes
- Sprite — using textures with sprites
- Bitmap Text — rendering text with bitmap fonts
- Spine Animation — Spine skeletal animation