ECS Lifecycle
Updated: Sep 4, 2026
IWSDK owns one render loop and runs ECS systems in a defined order. Use system lifecycle methods for application behavior and world.onXRFrame() only for work that requires the live WebXR frame.
When World.create(container, options) resolves, IWSDK has:
- Registered the core
Transform and Visibility components and systems. - Created the Three.js scene, camera, renderer, and player rig.
- Initialized input, audio, spatial UI, and requested optional feature systems.
- Initialized the asset manager, awaited critical/default assets, started background loads, and left lazy assets for first use.
- Loaded the requested native scene, or created an empty level.
- Started the renderer animation loop.
Lighting and environment data come from the authored scene. IWSDK does not add missing dome, image-based lighting, or light components.
const world = await World.create(container, {
level: '/scenes/main.iwsdk.scene.json',
});
A system defines lifecycle methods in its subclass body.
class MotionSystem extends createSystem({
moving: { required: [Transform, Velocity] },
}) {
init() {
this.cleanupFuncs.push(
this.queries.moving.subscribe('qualify', (entity) => {
prepareMotion(entity);
}),
);
}
update(delta: number, time: number) {
for (const entity of this.queries.moving.entities) {
updateMotion(entity, delta, time);
}
}
destroy() {
releaseMotionResources();
super.destroy();
}
}
init() runs when the system is initialized.update(delta, time) runs once per World update.destroy() releases system-owned resources. An override must call super.destroy() so callbacks in cleanupFuncs run.- Query
qualify and disqualify subscriptions report membership changes.
Update visibility state
↓
Run world.update(delta, time)
↓
Invoke world.onXRFrame callbacks when an XRFrame exists
↓
Render the scene
Systems run in ascending priority inside world.update(). IWSDK registers locomotion at -5, input at -4, canvas pointers at -3.5 when enabled, and grabbing at -3 when enabled.
Use a system’s update() method for application logic. It runs in browser and immersive modes and participates in ECS priority ordering.
Use world.onXRFrame() when code requires the live XRFrame, such as hit-test results or depth samples.
const stopReadingFrames = world.onXRFrame((frame, delta, time) => {
updateFromXRFrame(frame, delta, time);
});
// During cleanup:
stopReadingFrames();
The callback runs only when an immersive XR frame exists. It runs after ECS systems and before rendering. Do not start another requestAnimationFrame() or replace IWSDK’s animation loop.
Load a native scene from a URL with loadLevel():
await world.loadLevel('/scenes/second-room.iwsdk.scene.json');
Load an in-memory native scene document with loadSceneDocument():
await world.loadSceneDocument(sceneDocument);
Runtime scene loading accepts only import-free documents. Flatten an authoring
composition before passing its URL or document to the World.
Level-scoped entities are replaced with the active level. Entities created with { persistent: true } remain under the scene root.
See
World for the APIs that control scenes and XR sessions.