Develop

ECS Architecture in WebXR

Updated: Sep 4, 2026
IWSDK uses an entity-component-system (ECS) runtime to keep data separate from behavior. This structure lets a system process only the entities that match its query.

Data-oriented storage

A component defines fields. The ECS stores each field across all entities instead of storing a separate behavior object for every scene object.
Health.current: [100, 25, 80, ...]
Health.max:     [100, 100, 150, ...]
                 entity 0  1    2
Keep component schemas focused. Put behavior in systems and use queries to select the data each system needs.

Queries and systems

The following example defines every application-owned component it uses. Transform and Visibility are IWSDK components.
import {
  Transform,
  Types,
  Visibility,
  createComponent,
  createSystem,
  eq,
} from '@iwsdk/core';

const NeedsUpdate = createComponent('NeedsUpdate', {
  dirty: { type: Types.Boolean, default: true },
});

class VisibilityWorkSystem extends createSystem({
  targets: {
    required: [Transform, Visibility, NeedsUpdate],
    where: [
      eq(Visibility, 'isVisible', true),
      eq(NeedsUpdate, 'dirty', true),
    ],
  },
}) {
  update() {
    for (const entity of this.queries.targets.entities) {
      // Perform the application-owned work once.
      entity.setValue(NeedsUpdate, 'dirty', false);
    }
  }
}
Query membership updates when components are added or removed. A scalar field changed through setValue() also causes dependent value predicates to be reevaluated.

Composition

Add components to combine independent behaviors. Use the public components exported by IWSDK, and define application-specific data in the app.
import {
  OneHandGrabbable,
  PhysicsBody,
  RayInteractable,
} from '@iwsdk/core';

entity.addComponent(RayInteractable);
entity.addComponent(OneHandGrabbable);
entity.addComponent(PhysicsBody);
The input, grab, and physics systems respond to the components they own. Application systems can add their own state without subclassing the entity.

Transform and Three.js

world.createTransformEntity() associates an ECS entity with a Three.js Object3D and a Transform component. IWSDK replaces the object’s position, quaternion, rotation, and scale values with synchronized views over the component storage.
entity.object3D!.position.x += 0.1;

const position = entity.getVectorView(Transform, 'position');
console.log(position[0]);
Both lines address the same transform data. TransformSystem also keeps ECS parent references and the Three.js hierarchy aligned.

Frame order

Systems run in ascending numeric priority. More-negative priorities run first. IWSDK assigns these priorities when the related features are enabled:
-5.0  LocomotionSystem
-4.0  InputSystem
-3.5  CanvasPointerSystem
-3.0  GrabSystem
 0.0  Default application systems
After systems update, IWSDK invokes live XR-frame callbacks and renders the scene. Choose an application-system priority from the behavior it depends on; do not rely on an undocumented priority.

Profiling

Profile representative content on the target headset. Query size is one useful signal:
console.debug('Visible targets', this.queries.targets.entities.size);
Use browser performance and memory tools for frame timing, allocations, and GPU work.
See Systems and Queries for the APIs used here.