Skip to content

Custom runtime integration ​

Use the orchestration API directly when building a platform runtime, editor tool, test harness, or server-side asset pipeline. JollyPixel game projects can normally rely on @jolly-pixel/runtime instead.

Compose the loading system ​

Define one shared AssetType token for each kind. Register its loader, then construct a coordinator from the registry and catalog:

ts
import {
  AssetCatalog,
  AssetCoordinator,
  AssetLoaderRegistry,
  AssetRecord,
  AssetReference,
  AssetType
} from "@jolly-pixel/asset";

interface Model {
  readonly path: string;
}

const MODEL_ASSET = new AssetType<Model>("model");
const catalog = new AssetCatalog([
  new AssetRecord({
    id: "hero-model",
    kind: "model",
    path: "project:/models/hero.glb"
  })
]);

const loaders = new AssetLoaderRegistry();
loaders.register(MODEL_ASSET, {
  async load(record) {
    return {
      path: record.path.toString()
    };
  }
});

const assets = new AssetCoordinator({
  catalog,
  loaders
});

The same MODEL_ASSET object must be used to create references and register the loader. Recreating new AssetType<Model>("model") with the same kind does not satisfy token identity checks.

Load behind an asynchronous boundary ​

request() creates a handle without starting I/O. A batch prepares the fixed set of references needed by an operation:

ts
const heroReference = new AssetReference(
  "hero-model",
  MODEL_ASSET
);
const hero = assets.request(heroReference);
const batch = assets.loadBatch([heroReference]);

await batch.done;

const model = hero.get();

Concurrent batches share the coordinator's in-flight promise. Each batch still owns its progress and failure state.

For one explicit dynamic asset, load() returns the value and accepts an optional abort signal:

ts
const controller = new AbortController();
const model = await assets.load(heroReference, {
  signal: controller.signal
});

The loader decides how to apply the signal. loadBatch() accepts the same signal option and passes it to every loader it starts.

Replace loaded content ​

Replacing a catalog record does not change a value already loaded by the coordinator. Evict it explicitly and dispose the returned platform resource when required:

ts
catalog.set(updatedRecord);

const value = assets.evict(heroReference);
disposeModel(value);

evict() does not abort in-flight I/O or dispose values.