Chapter 4: External Assets
Updated: Sep 4, 2026
IWSDK loads project assets through a shared catalog. The runtime and native scene editor use the same IDs. The catalog can contain models, textures, HDR images, audio, UIKitML documents, and Three.js objects.
Create or update src/assets.ts:
import { AssetType, defineAssets } from '@iwsdk/core';
const publicAssetUrl = (path: string): string =>
`${import.meta.env.BASE_URL}${path.replace(/^\/+/u, '')}`;
export default defineAssets({
robot: {
name: 'Robot',
type: AssetType.GLTF,
url: publicAssetUrl('gltf/robot.glb'),
priority: 'critical',
},
logo: {
name: 'Logo',
type: AssetType.Texture,
url: publicAssetUrl('textures/logo.png'),
priority: 'background',
},
'settings-panel': {
name: 'Settings panel',
type: AssetType.UIKitML,
url: publicAssetUrl('ui/settings.uikitml'),
priority: 'lazy',
},
});
Select this module in iwsdk.config.json:
{
"version": "iwsdk.project.v1",
"scene": "./public/scenes/main.iwsdk.scene.json",
"assets": {
"module": "./src/assets"
},
"world": {
"xr": {
"mode": "vr"
}
}
}
Use import.meta.env.BASE_URL for files under public/. It keeps URLs valid when the app uses a deployment base path.
AssetType includes:
AssetType.GLTF for GLB and GLTF models.AssetType.Texture for image textures.AssetType.HDRTexture for HDR or EXR environment maps.AssetType.Audio for audio buffers.AssetType.UIKitML for spatial UI documents.
Convert unsupported model formats to GLB or GLTF before adding them to the catalog.
Choose a loading priority
Each URL asset can set a priority:
critical: Loads before World.create() resolves.background: Starts after critical assets and does not block world creation.lazy: Loads when code or a scene first uses the asset.
An omitted priority acts as critical.
Each cached asset request has a 30-second timeout. A critical failure rejects World.create() with an AssetLoadError. Background failures are logged. Lazy and explicit loads reject their caller.
A critical model is available after world creation:
import { AssetManager, World } from '@iwsdk/core';
import projectOptions from 'virtual:iwsdk-project';
const world = await World.create(
document.getElementById('scene-container') as HTMLDivElement,
projectOptions,
);
const robot = AssetManager.getGLTF('robot');
if (robot != null) {
robot.scene.position.set(0, 1, -3);
world.createTransformEntity(robot.scene);
}
AssetManager.getGLTF() returns a clone that can be placed in the scene.
For a lazy model, load it by ID before reading it:
await AssetManager.loadGLTFById('robot');
const robot = AssetManager.getGLTF('robot');
A native scene can also instantiate the catalog entry:
{
"id": "robot",
"content": {
"type": "asset",
"asset": "robot"
},
"transform": {
"position": [0, 1, -3]
}
}
Prepare models and textures
Vite copies files from public/ without changing them. Optimize assets before adding them to the project.
For models:
- Remove unused geometry.
- Reduce polygon counts for the target device.
- Compress textures and other resources.
- Export to GLB when a single binary file is easier to deploy.
For textures:
- Use JPEG for opaque photos.
- Use PNG or WebP when alpha is required.
- Match texture dimensions to the visible size of the object.
- Verify color-space settings in the material.
Blender can import formats such as FBX or OBJ and export GLB or GLTF.
Deploy local and remote assets
Keep all files referenced by a GLTF together. This includes buffers and textures. Test the production build with its final base path.
Remote assets need HTTPS and valid CORS headers. For reliable production use, host critical assets with the app or mirror them on infrastructure that you control.
The IWSDK examples use a fixed release of @iwsdk/example-assets. You can point the example asset base at a local mirror:
VITE_IWSDK_EXAMPLE_ASSET_BASE_URL=/vendor/iwsdk-assets
Preserve the package’s assets/<asset-id>/<file> layout in that mirror.