Develop

ECS ↔ Three.js Interop

Updated: Sep 4, 2026
IWSDK links ECS entities to Three.js objects through Transform. The object’s transform properties are synchronized views over ECS storage; the runtime does not copy one representation over the other on every frame.

Create a linked entity

import { Object3D, Transform, World } from '@iwsdk/core';

const world = await World.create(container);
const object = new Object3D();
const entity = world.createTransformEntity(object);

console.log(entity.object3D === object); // true
console.log(entity.hasComponent(Transform)); // true
When the entity qualifies for TransformSystem, IWSDK associates its object with the entity index and installs synchronized position, quaternion, rotation, and scale objects.

Update a transform

Direct Three.js updates and ECS vector-view updates address the same storage.
entity.object3D!.position.x += 1;

const position = entity.getVectorView(Transform, 'position');
position[1] += 1;

entity.getVectorView(Transform, 'scale').set([2, 2, 2]);
setValue() does not accept vector fields. Vector-view and Object3D transform changes also do not notify value predicates. When a query must react to derived transform state, store that state in a scalar application-component field and update the scalar with setValue().

Use Three.js for rendering data

Create geometry, materials, and non-entity child objects with Three.js.
const root = world.createTransformEntity();
const body = new Mesh(bodyGeometry, bodyMaterial);
const leftWheel = new Mesh(wheelGeometry, wheelMaterial);

root.object3D!.add(body, leftWheel);
leftWheel.position.set(-1, -0.5, 1.2);
The child mesh does not become an ECS entity merely because it is added to an entity’s Object3D hierarchy. Create another transform entity when the child needs its own components and queries.

Parent transform entities

Pass the parent entity when creating the child.
const parent = world.createTransformEntity();
const child = world.createTransformEntity(undefined, { parent });

console.log(child.getValue(Transform, 'parent') === parent); // true
console.log(child.object3D!.parent === parent.object3D); // true
Transform.parent is an entity reference. Its packed representation uses an index internally, but getValue() returns the entity.

Choose level or persistent lifetime

const levelObject = world.createTransformEntity();
const persistentObject = world.createTransformEntity(undefined, {
  persistent: true,
});
Level objects attach below the active level. Persistent objects attach below the scene root and remain when a new level loads.

Camera coordinates

world.camera.position is local to world.player. Ask Three.js for the world-space viewer position when comparing it with a world-space object.
const viewerPosition = world.camera.getWorldPosition(new Vector3());
const objectPosition = entity.object3D!.getWorldPosition(new Vector3());
const distance = viewerPosition.distanceTo(objectPosition);

Frame behavior

Application systems update in numeric priority order. The transform system maintains synchronized accessors and parenting. IWSDK then invokes XR-frame callbacks when applicable and renders the scene.
Do not create a second render loop for transform synchronization.