@gameable/assets
Classes
AssetError
An asset operation that could not be completed.
Extends
Error
Constructors
Constructor
ts
new AssetError(
assetId,
message,
options?
): AssetError;Build an asset error.
Parameters
| Parameter | Type | Description |
|---|---|---|
assetId | string | Asset id involved. |
message | string | What went wrong. |
options? | ErrorOptions | Standard Error options, used to keep the cause. |
Returns
Overrides
ts
Error.constructorProperties
assetId
ts
readonly assetId: string;The id involved, when there is one.
code?
ts
readonly optional code?: string;Stable diagnostic code when integrity verification fails.
ColliderFormatError
A collider buffer that could not be read.
The message always says what was expected and what was found, because the only realistic way to hit it is to hand the parser the wrong file.
Extends
Error
Constructors
Constructor
ts
new ColliderFormatError(message): ColliderFormatError;Build a collider format error.
Parameters
| Parameter | Type | Description |
|---|---|---|
message | string | What is wrong with the buffer. |
Returns
Overrides
ts
Error.constructorGameAddressError
An unsafe game address and the reason it was refused.
Example
ts
import { GameAddressError, resolveGameAddress } from 'gameable/assets';
try { resolveGameAddress('javascript:alert(1)'); }
catch (error) { if (error instanceof GameAddressError) console.log(error.reason); }Extends
Error
Constructors
Constructor
ts
new GameAddressError(address, reason): GameAddressError;Parameters
| Parameter | Type |
|---|---|
address | string |
reason | string |
Returns
Overrides
ts
Error.constructorProperties
address
ts
readonly address: string;The refused address.
reason
ts
readonly reason: string;Why the address is unsafe.
ManifestError
A manifest that failed validation.
path is a JSON-pointer-ish location such as assets[2].collider.radius, so the message alone is enough to find and fix the problem.
Example
ts
import { ManifestError, parseManifest } from 'gameable/assets';
try {
parseManifest({ version: 1 });
} catch (err) {
if (err instanceof ManifestError) console.error(err.path, err.message);
}Extends
Error
Constructors
Constructor
ts
new ManifestError(path, message): ManifestError;Build a manifest validation error.
Parameters
| Parameter | Type | Description |
|---|---|---|
path | string | Location of the offending value, for example assets[0].id. |
message | string | What is wrong with it. |
Returns
Overrides
ts
Error.constructorProperties
path
ts
readonly path: string;Location of the offending value inside the manifest.
Interfaces
AssetCollider
Static collision proxy registered with physics when the asset loads.
Properties
halfExtents?
ts
readonly optional halfExtents?: Vec3;Box half extents. Required when shape is box.
height?
ts
readonly optional height?: number;Capsule height. Required for capsule.
layer?
ts
readonly optional layer?: ColliderLayer;Physics layer. Defaults to static.
offset?
ts
readonly optional offset?: Vec3;Offset from the entity origin.
radius?
ts
readonly optional radius?: number;Sphere or capsule radius. Required for sphere and capsule.
shape
ts
readonly shape: ColliderShape;Collider shape.
src?
ts
readonly optional src?: string;Path to collision geometry, for shapes that need their own mesh.
AssetEntry
One manifest entry.
Properties
aliases?
ts
readonly optional aliases?: readonly string[];Previous ids that continue to resolve to this entry.
bakedLighting?
ts
readonly optional bakedLighting?: string;A splat's baked lighting (a .light.bin the bake editor wrote for this very splat), drawn with it. Relative path or absolute URL like src. Only on splat entries.
collider?
ts
readonly optional collider?: AssetCollider;Static collision proxy.
id
ts
readonly id: string;Stable identifier used by game logic. Keep previous names in aliases.
lightProbes?
ts
readonly optional lightProbes?: string;A splat's light probe grid (a .probes.json the bake editor wrote): the light at each place in it, which characters standing there are lit by. Relative path or absolute URL like src. Only on splat entries.
mutable?
ts
readonly optional mutable?: boolean;Leave a remote source live when bundling (explicit opt-out from pinning).
poster?
ts
readonly optional poster?: string;A still shown until a video's first frame (a cutscene's poster). Relative path or absolute URL like src. Only on video entries.
rig?
ts
readonly optional rig?: AssetRig;Character rig configuration. Required when type is character.
sha256?
ts
readonly optional sha256?: string;SHA-256 of immutable source bytes, verified before loading.
shadowSources?
ts
readonly optional shadowSources?: string;A splat's shadow sources (a .shadows.json the bake editor wrote): where the shadows of what stands in the place fall from. Relative path or absolute URL like src. Only on splat entries.
src
ts
readonly src: string;Relative path (resolved against baseUrl) or absolute URL.
tags?
ts
readonly optional tags?: readonly string[];Free-form labels for grouping and preload policies.
type
ts
readonly type: AssetType;What the host should load this as.
AssetLoaderContext
Everything a loader is given besides the URL.
Properties
bytes?
ts
readonly optional bytes?: ArrayBuffer;Verified source bytes for pinned entries. Load these instead of fetching the URL again.
manifest
ts
readonly manifest: AssetManifest;The manifest the entry came from.
registry
ts
readonly registry: AssetRegistry;The registry doing the loading, for loaders that need sibling assets.
signal?
ts
readonly optional signal?: AbortSignal;Abort signal, when the caller supplied one.
AssetLook
How an exported studio character (aosrig-splat) is drawn, when the manifest says: the point shape ('size-aware', the engine's point while small on screen and the studio's once large; 'engine', three's own 2-sigma point; 'studio', the studio page's; 'full-opacity', the small-point rule alone) and where its colours add up ('stored', in the file's own space as the studio adds them; 'linear', in linear light). Nothing asked = 'size-aware' and 'stored', chosen by Isaac on the looks sheet of 30 Sep 2026; a manifest entry overrides the package's own look, and the bridge's extras override both.
Properties
blend?
ts
readonly optional blend?: "stored" | "linear";pointShape?
ts
readonly optional pointShape?: "engine" | "studio" | "full-opacity" | "size-aware";AssetManifest
A parsed, validated assets.json.
Properties
assets
ts
readonly assets: readonly AssetEntry[];Every asset the game can reference.
baseUrl
ts
readonly baseUrl: string;Prefix joined to every relative src.
exclude?
ts
readonly optional exclude?: readonly string[];Files never bundled, as globs relative to the manifest folder.
include?
ts
readonly optional include?: readonly string[];Extra files to bundle, as globs relative to the manifest folder.
version
ts
readonly version: 1;Manifest schema version. 1 is the only value today.
AssetProgress
One step of a AssetRegistry.preload run.
Properties
error?
ts
readonly optional error?: unknown;The failure, when ok is false.
id
ts
readonly id: string;Id that just finished.
loaded
ts
readonly loaded: number;Entries finished so far, including failures.
ok
ts
readonly ok: boolean;Whether the entry loaded successfully.
total
ts
readonly total: number;Entries in this run.
AssetRegistry
Resolves ids to handles, entries, URLs and loaded objects.
Properties
ids
ts
readonly ids: readonly string[];Every declared id, in manifest order.
manifest
ts
readonly manifest: AssetManifest;The manifest this registry was built from.
Methods
add()
ts
add(more): string[];Add another manifest's entries while the engine runs: a world package, or anything else that arrives after the game's own manifest was read.
Each entry gets the next handle, so every handle already given out stays what it was, and each src resolves against the added manifest's own baseUrl. An id the registry already holds keeps its first entry and is returned among the skipped, so loading the same world twice is harmless.
Parameters
| Parameter | Type | Description |
|---|---|---|
more | AssetManifest | A parsed manifest (parseManifest). |
Returns
string[]
The ids that were already declared and were left as they were.
dispose()
ts
dispose(): void;Drop every cached object and in-flight promise.
Returns
void
Nothing.
entry()
ts
entry(idOrHandle): AssetEntry | undefined;The manifest entry for an id or a handle.
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
Returns
AssetEntry | undefined
The entry, or undefined when unknown.
get()
ts
get(idOrHandle): unknown;The already-loaded object for an id.
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
Returns
unknown
The object, or undefined when it is not loaded yet.
hasLoader()
ts
hasLoader(type): boolean;Whether a loader is registered for a type.
Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Asset type. |
Returns
boolean
True when a loader exists.
idOf()
ts
idOf(handle): string | undefined;The id behind a handle.
Parameters
| Parameter | Type | Description |
|---|---|---|
handle | number | Handle from resolve. |
Returns
string | undefined
The id, or undefined for 0 and out-of-range handles.
load()
ts
load(idOrHandle): Promise<unknown>;Load an asset, or return the promise for one already loading or loaded.
Repeat calls for the same id share one promise — the in-flight one while it is loading, then the resolved one forever after — so a hot path may call this every frame without allocating. An unknown id rejects with a shared AssetError, which is deliberately the same error object every time.
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
Returns
Promise<unknown>
The loaded object.
onProgress()
ts
onProgress(cb): () => void;Subscribe to preload progress.
Parameters
| Parameter | Type | Description |
|---|---|---|
cb | (progress) => void | Called once per finished entry. |
Returns
An unsubscribe function.
() => void
poster()
ts
poster(idOrHandle): string | undefined;The resolved URL of a video entry's poster (the still shown until its first frame).
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
Returns
string | undefined
The URL, or undefined when unknown or when the entry has no poster.
preload()
ts
preload(tag?): Promise<void>;Load every asset, or every asset carrying tag.
Parameters
| Parameter | Type | Description |
|---|---|---|
tag? | string | Optional tag filter. |
Returns
Promise<void>
Resolves once the whole set has settled.
registerLoader()
ts
registerLoader(type, loader): void;Register (or replace) the loader for an asset type.
This is how gameable/splat and gameable/character add the splat and character types without core depending on them.
Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Asset type the loader handles. |
loader | AssetLoader | The loader. |
Returns
void
Nothing.
resolve()
ts
resolve(id): number;The stable handle for an id.
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Asset id. |
Returns
number
A 1-based handle, or 0 when the id is not declared.
url()
ts
url(idOrHandle): string | undefined;The resolved URL for an id or a handle.
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
Returns
string | undefined
The URL, or undefined when unknown.
urlFor()
ts
urlFor(idOrHandle, path): string | undefined;A path resolved as an entry's src is: against the baseUrl of the manifest the entry came from. For a file named beside an asset (a splat's bakedLighting).
Parameters
| Parameter | Type | Description |
|---|---|---|
idOrHandle | string | number | Asset id or handle. |
path | string | Relative path or absolute URL. |
Returns
string | undefined
The URL, or undefined when the asset is unknown.
AssetRig
Character rig configuration.
Properties
backend
ts
readonly backend: RigBackend;Which backend drives the character.
'skinned' means src is itself a glTF/GLB carrying JOINTS_0/WEIGHTS_0 and its animations, so no AssetRig.pack is involved.
clips?
ts
readonly optional clips?: readonly string[];More clip files the character plays besides its own (aosrig-splat version 2: files of its clips.json format), each resolved like pack: beside src, or absolute.
controlNames?
ts
readonly optional controlNames?: readonly string[];Ordered rig control names; the index is the wire format.
expressionSpace?
ts
readonly optional expressionSpace?: ExpressionSpace;Layout of the expression vector.
height?
ts
readonly optional height?: number;The character's real height, metres, floor to the crown of its own points (hair and all). The studio makes every character about 1.75 m tall; a game that knows the person's height says it here, and the engine draws the character at it: its points, its joints, its physics body, its shadow and its measures all at that scale. Absent: as exported.
light?
ts
readonly optional light?: string;A lighter copy of the same character (aosrig-splat: its character.json, resolved like pack): drawn first, so the character stands sooner, src taking over once it is down, or all a phone loads. The character bridge's loading order does this by itself.
look?
ts
readonly optional look?: AssetLook;How an aosrig-splat character is drawn, when the game says (see AssetLook).
motion?
ts
readonly optional motion?: string | false;How an aosrig-splat character moves: false turns the movement system off for this character (it walks by the clips' blend, as before); a path names its own motion set (a soma-motion file, text or the compact binary), resolved like pack, when its character.json does not. Absent: the package's own set if it names one, laid over the engine's shared set.
pack?
ts
readonly optional pack?: string;Path to the baked .aosrig pack. Required when backend is gnm.
vertexCount?
ts
readonly optional vertexCount?: number;Vertices the rig stage produces per frame.
AudioLoaderOptions
Options accepted by createAudioLoader.
Extended by
Properties
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};fetch implementation. Defaults to the global one.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
ColliderMesh
A triangle mesh: flat positions plus flat triangle indices.
Properties
indices
ts
readonly indices: Uint32Array;Triangle indices into positions. Length is a multiple of three.
positions
ts
readonly positions: Float32Array;Vertex positions in metres, flattened x y z. Length is 3 * vertexCount.
CreateAssetRegistryOptions
Options accepted by createAssetRegistry.
Properties
loaders?
ts
readonly optional loaders?: Readonly<Record<string, AssetLoader>>;Loaders by asset type. More can be added later with registerLoader.
manifest
ts
readonly manifest: AssetManifest;The validated manifest.
signal?
ts
readonly optional signal?: AbortSignal;Abort signal handed to every loader.
DefaultLoaderOptions
Options accepted by createDefaultLoaders.
Extends
Properties
dracoDecoderPath?
ts
readonly optional dracoDecoderPath?: string;Directory holding draco_decoder.js / .wasm, served by your app.
Defaults to DEFAULT_DRACO_DECODER_PATH. Copy the files from three/examples/jsm/libs/draco/ into your public/ directory, or point this at a CDN.
Inherited from
GltfLoaderOptions.dracoDecoderPath
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};fetch implementation. Defaults to the global one.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
Inherited from
ktx2TranscoderPath?
ts
readonly optional ktx2TranscoderPath?: string;Directory holding the Basis Universal transcoder. When omitted, KTX2 textures are not supported and no KTX2Loader is constructed.
Inherited from
GltfLoaderOptions.ktx2TranscoderPath
renderer?
ts
readonly optional renderer?: WebGPURenderer;An initialised renderer. Required for KTX2: detectSupport needs to know which compressed texture formats the backend exposes.
Inherited from
DefaultLoaders
The default loader set plus the handle needed to release three's workers.
Properties
loaders
ts
readonly loaders: Record<string, AssetLoader>;Pass straight to createAssetRegistry({ loaders }).
Methods
dispose()
ts
dispose(): void;Terminate any worker pools the loaders started.
Returns
void
Nothing.
ExpressionSegment
A named contiguous span of an expression vector.
Properties
length
ts
readonly length: number;Number of components in the span.
name
ts
readonly name: string;Segment name, for example left_eye.
offset
ts
readonly offset: number;First index of the span.
ExpressionSpace
Layout of the expression vector a character rig consumes.
Properties
arkitMap?
ts
readonly optional arkitMap?: string;Path to an ARKit-52 to this-space mapping, when kind is not arkit52.
dim
ts
readonly dim: number;Number of components in the vector.
kind
ts
readonly kind: ExpressionSpaceKind;Which well-known expression space this is.
names?
ts
readonly optional names?: readonly string[];Optional per-component names.
segments?
ts
readonly optional segments?: readonly ExpressionSegment[];Optional named spans of the vector.
GltfLoaderHandle
A gltf loader plus the handles needed to release its workers.
Properties
load
ts
readonly load: AssetLoader;Register this under the gltf asset type.
Methods
dispose()
ts
dispose(): void;Terminate the Draco and KTX2 worker pools.
Returns
void
Nothing.
GltfLoaderOptions
Options accepted by createGltfLoader.
Extended by
Properties
dracoDecoderPath?
ts
readonly optional dracoDecoderPath?: string;Directory holding draco_decoder.js / .wasm, served by your app.
Defaults to DEFAULT_DRACO_DECODER_PATH. Copy the files from three/examples/jsm/libs/draco/ into your public/ directory, or point this at a CDN.
ktx2TranscoderPath?
ts
readonly optional ktx2TranscoderPath?: string;Directory holding the Basis Universal transcoder. When omitted, KTX2 textures are not supported and no KTX2Loader is constructed.
renderer?
ts
readonly optional renderer?: WebGPURenderer;An initialised renderer. Required for KTX2: detectSupport needs to know which compressed texture formats the backend exposes.
LoadManifestOptions
Options accepted by loadManifest.
Extends
Properties
baseUrl?
ts
readonly optional baseUrl?: string;Fallback baseUrl when the manifest does not declare one.
Inherited from
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};fetch implementation. Defaults to the global one.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
signal?
ts
readonly optional signal?: AbortSignal;Abort signal forwarded to fetch.
ParseColliderOptions
Options for parseCollider.
Properties
validate?
ts
readonly optional validate?: boolean;Walk the index array and reject an index that points past the last vertex.
The scan is O(indexCount) on the asset-loading path, and it catches exactly one thing: a corrupt or mis-generated file. That is a development concern, so it defaults to import.meta.env.DEV — true under vite dev, false in a production build — and to true wherever there is no import.meta.env at all (Node, vitest, a plain bundler), so the safe answer is the one you get by saying nothing.
Turning it off does not make a bad index safe: Jolt reads the array itself. It moves the check to the place that already has to do it.
ParseManifestOptions
Options accepted by parseManifest.
Extended by
Properties
baseUrl?
ts
readonly optional baseUrl?: string;Fallback baseUrl when the manifest does not declare one.
Type Aliases
AssetLoader
ts
type AssetLoader = (url, entry, ctx) => Promise<unknown>;Turns one asset entry into a loaded object.
One loader is registered per AssetEntry.type. What it resolves to is the loader's business: gltf yields a GLTF, audio an ArrayBuffer, splat and character whatever their packages define.
Parameters
| Parameter | Type |
|---|---|
url | string |
entry | AssetEntry |
ctx | AssetLoaderContext |
Returns
Promise<unknown>
AssetLoaderMap
ts
type AssetLoaderMap = Readonly<Record<string, AssetLoader>>;Loaders keyed by asset type.
AssetType
ts
type AssetType = "splat" | "gltf" | "character" | "audio" | "video";What the host should load an entry as. video is a clip the page plays over the stage (a cutscene): the host never decodes it itself, a <video> does.
ColliderLayer
ts
type ColliderLayer = "static" | "dynamic" | "trigger";Physics layer a collider belongs to.
ColliderShape
ts
type ColliderShape = "mesh" | "box" | "sphere" | "capsule" | "convex-hull" | "heightfield";Collision proxy shapes understood by the physics module.
ExpressionSpaceKind
ts
type ExpressionSpaceKind = "arkit52" | "gnm" | "gnm68";Coordinate space a character's expression vector is expressed in.
RigBackend
ts
type RigBackend = "orl" | "gnm" | "skinned" | "aosrig-splat";Which RigBackend drives a character.
orl and gnm are splat head rigs and need a baked pack; skinned is a whole body — a glTF/GLB with JOINTS_0/WEIGHTS_0 and its clips embedded, so it needs no pack at all.
Vec3
ts
type Vec3 = readonly [number, number, number];A three-component vector, as written in the manifest.
Variables
DEFAULT_DRACO_DECODER_PATH
ts
const DEFAULT_DRACO_DECODER_PATH: "/draco/" = '/draco/';Where DRACOLoader looks for its decoder, when none is configured.
PACKAGE
ts
const PACKAGE: "@gameable/assets";Package identity marker.
Example
ts
import { PACKAGE } from 'gameable/assets';
console.log(PACKAGE); // 'gameable/assets'SITE_ROOTS
ts
const SITE_ROOTS: readonly string[];Site-root mounts for rooms, visit and talk services and the dev server's local characters. Like Unreal's /Script/ versus /Game/, these belong to the host, rather than a published game's folder.
Example
ts
import { SITE_ROOTS } from 'gameable/assets';
console.log(SITE_ROOTS.includes('services'));Functions
assetUrl()
ts
function assetUrl(id): string;Resolve an id or alias using the most recently created live asset registry.
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Manifest asset id. |
Returns
string
Its game-root-aware URL; throws when the registry or id is missing.
Example
ts
import { assetUrl } from 'gameable';
const response = await fetch(assetUrl('arena'));
console.log(response.ok);createAssetRegistry()
ts
function createAssetRegistry(options): AssetRegistry;Build an asset registry over a validated manifest.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | CreateAssetRegistryOptions | Manifest, loaders and an optional abort signal. |
Returns
The registry.
Example
ts
import { createAssetRegistry, parseManifest } from 'gameable/assets';
const manifest = parseManifest({
version: 1,
baseUrl: 'assets/',
assets: [{ id: 'shot', type: 'audio', src: 'sfx/shot.ogg', tags: ['sfx'] }],
});
const assets = createAssetRegistry({ manifest, loaders: {} });
console.log(assets.resolve('shot')); // 1
console.log(assets.resolve('nope')); // 0createAudioLoader()
ts
function createAudioLoader(options?): AssetLoader;Build the audio asset loader.
It resolves to the raw ArrayBuffer. Turning that into an AudioBuffer is gameable/audio's job, because decoding needs an AudioContext and the registry must stay usable without one.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | AudioLoaderOptions | fetch override. |
Returns
An asset loader resolving to an ArrayBuffer.
Example
ts
import { createAssetRegistry, createAudioLoader, parseManifest } from 'gameable/assets';
const manifest = parseManifest({ version: 1, assets: [{ id: 'shot', type: 'audio', src: 'shot.ogg' }] });
const assets = createAssetRegistry({ manifest, loaders: { audio: createAudioLoader() } });
const bytes = (await assets.load('shot')) as ArrayBuffer;
console.log(bytes.byteLength > 0);createDefaultLoaders()
ts
function createDefaultLoaders(options?): DefaultLoaders;The loaders createEngine registers: gltf and audio.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | DefaultLoaderOptions | Draco/KTX2 paths and a fetch override. |
Returns
The loader map and its dispose.
Example
ts
import { createAssetRegistry, createDefaultLoaders, parseManifest } from 'gameable/assets';
const defaults = createDefaultLoaders({ dracoDecoderPath: '/draco/' });
const manifest = parseManifest({ version: 1, assets: [] });
const assets = createAssetRegistry({ manifest, loaders: defaults.loaders });
console.log(assets.hasLoader('audio')); // truecreateGltfLoader()
ts
function createGltfLoader(options?): GltfLoaderHandle;Build the gltf asset loader.
The underlying GLTFLoader, DRACOLoader and optional KTX2Loader are constructed lazily, on the first asset that needs them.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | GltfLoaderOptions | Draco decoder path, optional KTX2 transcoder path and renderer. |
Returns
The loader function and its dispose.
Example
ts
import { createAssetRegistry, createGltfLoader, parseManifest } from 'gameable/assets';
const gltf = createGltfLoader({ dracoDecoderPath: '/draco/' });
const manifest = parseManifest({ version: 1, assets: [{ id: 'rock', type: 'gltf', src: 'rock.glb' }] });
const assets = createAssetRegistry({ manifest, loaders: { gltf: gltf.load } });
console.log(assets.hasLoader('gltf')); // trueencodeCollider()
ts
function encodeCollider(positions, indices): ArrayBuffer;Write an arena.collider.bin buffer.
The exact inverse of parseCollider: parseCollider(encodeCollider(p, i)) returns p and i unchanged, up to f32 rounding of the positions.
Parameters
| Parameter | Type | Description |
|---|---|---|
positions | ArrayLike<number> | Vertex positions in metres, flattened x y z. |
indices | ArrayLike<number> | Triangle indices into positions. |
Returns
ArrayBuffer
The encoded file, ready to write to disk.
Throws
When positions is not a whole number of vertices, indices is not a whole number of triangles, or an index is out of range.
Example
ts
import { encodeCollider, parseCollider } from 'gameable/assets';
// A single 1 m triangle on the floor.
const file = encodeCollider([0, 0, 0, 1, 0, 0, 0, 0, 1], [0, 1, 2]);
console.log(file.byteLength); // 8 + 9 * 4 + 3 * 4 = 56
console.log(parseCollider(file).indices.length); // 3findEntry()
ts
function findEntry(manifest, id): AssetEntry | undefined;Look an entry up by id.
The lookup table is built once per manifest object and cached against it, so this is a Map hit rather than a scan after the first call.
Parameters
| Parameter | Type | Description |
|---|---|---|
manifest | AssetManifest | Manifest to search. |
id | string | Asset id. |
Returns
AssetEntry | undefined
The entry, or undefined when the id is unknown.
Example
ts
import { findEntry, parseManifest } from 'gameable/assets';
const manifest = parseManifest({ version: 1, assets: [{ id: 'shot', type: 'audio', src: 'a.ogg' }] });
console.log(findEntry(manifest, 'shot')?.src); // 'a.ogg'gameRoot()
ts
function gameRoot(): string;The game's folder, as an absolute URL ending in /, or / without a page.
Returns
string
The override, document folder, location folder, or /, in that order.
Example
ts
import { gameRoot } from 'gameable/assets';
console.log(gameRoot());gameUrl()
ts
function gameUrl(address): string;Sanitize and resolve a game file address, safely repeatable on its own output. game:x uses the game folder; /x is its legacy spelling. Bundler URLs already under the game folder and site service mounts retain their paths. Relative paths and http(s), data, blob and protocol-relative URLs are unchanged. File URLs are also unchanged when no browser page exists.
Parameters
| Parameter | Type | Description |
|---|---|---|
address | string | A game file address. |
Returns
string
The resolved address.
Throws
For controls, backslashes, unsafe schemes or paths escaping the game folder.
Example
ts
import { resolveGameAddress } from 'gameable/assets';
const response = await fetch(resolveGameAddress('game:worlds/attic/world.json'));
console.log(response.ok);joinUrl()
ts
function joinUrl(baseUrl, src): string;Join a base prefix and a relative source path. In the browser, exactly / means the game's document directory, so the same manifest works at the origin root and under a published site's prefix. Other prefixes and Node resolution keep their usual meaning.
Parameters
| Parameter | Type | Description |
|---|---|---|
baseUrl | string | Prefix; may be empty, a path, or an absolute URL. |
src | string | Entry src. |
Returns
string
The joined URL.
Example
ts
import { joinUrl } from 'gameable/assets';
console.log(joinUrl('https://cdn.example/a/b/', '../c.glb')); // 'https://cdn.example/a/c.glb'loadManifest()
ts
function loadManifest(url, options?): Promise<AssetManifest>;Fetch and validate an assets.json.
When the document omits baseUrl, it defaults to the manifest's own directory, exactly as the JSON Schema says.
Parameters
| Parameter | Type | Description |
|---|---|---|
url | string | Where the manifest lives. |
options | LoadManifestOptions | fetch override, abort signal and baseUrl fallback. |
Returns
Promise<AssetManifest>
The validated manifest.
Example
ts
import { loadManifest } from 'gameable/assets';
const manifest = await loadManifest('assets/assets.json');
console.log(manifest.assets.length);parseCollider()
ts
function parseCollider(buffer, options?): ColliderMesh;Read an arena.collider.bin buffer.
The header is always validated — counts and total length — so a mesh that parses has the right number of bytes in it. The per-index range scan is the one part that costs time proportional to the file, so it follows ParseColliderOptions.validate.
Parameters
| Parameter | Type | Description |
|---|---|---|
buffer | ArrayBuffer | ArrayBufferView<ArrayBufferLike> | The file contents, as an ArrayBuffer or any view over one. |
options | ParseColliderOptions | See ParseColliderOptions. |
Returns
The vertex positions and triangle indices, as views over buffer.
Throws
When the buffer is truncated, the index count is not a multiple of three, or (when validating) an index is out of range.
Example
ts
import { encodeCollider, parseCollider } from 'gameable/assets';
const bytes = encodeCollider([0, 0, 0, 1, 0, 0, 0, 0, 1], [0, 1, 2]);
const { positions, indices } = parseCollider(bytes);
console.log(positions.length / 3, indices.length / 3); // 3 1parseManifest()
ts
function parseManifest(json, options?): AssetManifest;Parse and validate an assets.json document.
Every failure throws a ManifestError naming the exact path, and the first failure wins: a manifest is either completely valid or rejected.
Parameters
| Parameter | Type | Description |
|---|---|---|
json | unknown | The parsed JSON document. |
options | ParseManifestOptions | Fallback baseUrl when the manifest omits one. |
Returns
A frozen, validated manifest.
Example
ts
import { parseManifest } from 'gameable/assets';
const manifest = parseManifest({
version: 1,
baseUrl: 'assets/',
assets: [{ id: 'arena', type: 'splat', src: 'arena.spz', tags: ['world'] }],
});
console.log(manifest.assets[0]?.id); // 'arena'resolveAssetUrl()
ts
function resolveAssetUrl(manifest, entry): string;Resolve an entry's src against the manifest's baseUrl.
Absolute URLs (https://…, data:…) and protocol-relative URLs (//host/x) are returned untouched, as are root-relative paths (/x.glb) when baseUrl is itself relative. When baseUrl carries a scheme, resolution goes through URL, so ../ segments collapse correctly.
Parameters
| Parameter | Type | Description |
|---|---|---|
manifest | AssetManifest | Manifest supplying baseUrl. |
entry | string | AssetEntry | An entry, or the id of one. |
Returns
string
The absolute or root-relative URL to fetch.
Example
ts
import { parseManifest, resolveAssetUrl } from 'gameable/assets';
const manifest = parseManifest({
version: 1,
baseUrl: 'assets',
assets: [{ id: 'arena', type: 'splat', src: 'arena.spz' }],
});
console.log(resolveAssetUrl(manifest, 'arena')); // 'assets/arena.spz'setGameRoot()
ts
function setGameRoot(root): void;Override the game folder for a host, worker or test. Pass null to clear it. Relative roots resolve against the current page.
Parameters
| Parameter | Type | Description |
|---|---|---|
root | string | null | The folder, or null to use the page again. |
Returns
void
Example
ts
import { setGameRoot } from 'gameable/assets';
setGameRoot('https://cdn.example/games/demo/site/');
setGameRoot(null);References
resolveGameAddress
Renames and re-exports gameUrl