Develop

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.

World initialization

When World.create(container, options) resolves, IWSDK has:
  1. Registered the core Transform and Visibility components and systems.
  2. Created the Three.js scene, camera, renderer, and player rig.
  3. Initialized input, audio, spatial UI, and requested optional feature systems.
  4. Initialized the asset manager, awaited critical/default assets, started background loads, and left lazy assets for first use.
  5. Loaded the requested native scene, or created an empty level.
  6. 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',
});

System lifecycle

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.

Per-frame order

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.

Choose a frame API

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.

Level lifecycle

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.