Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
Deep dive into entities: the fundamental building blocks of the ECS architecture. Learn about entity lifecycle, recycling, hierarchies, and metadata management.
In Web Engine's ECS architecture, entities are the simplest concept: they're just unsigned 32-bit integers that serve as unique identifiers. An entity has no data and no behavior on its own — it's merely an ID that ties components together.
Think of an entity as a database primary key. It's a number that identifies a game object, but all the actual data lives in component arrays indexed by that number.
import { addEntity, addComponent } from 'bitecs';import { Transform, RigidBody, Mesh } from '@web-engine-dev/core';// Create a new entityconst eid = addEntity(world);// eid is just a numberconsole.log(eid); // 42// Add components to give it data and behavioraddComponent(world, Transform, eid);addComponent(world, RigidBody, eid);addComponent(world, Mesh, eid);// Now set component dataTransform.position.x[eid] = 10.0;RigidBody.mass[eid] = 50.0;
Entity IDs are Sequential
bitECS assigns entity IDs sequentially starting from 0. The first entity is 0, second is 1, and so on. When entities are removed, their IDs are recycled to keep the arrays compact.
Entities go through a well-defined lifecycle from creation to destruction:
import { addEntity, addComponent } from 'bitecs';import { Transform, setEntityDisplayName } from '@web-engine-dev/core';// Create a new entityconst eid = addEntity(world);// Always add Transform (required for all game objects)addComponent(world, Transform, eid);// Set default transform valuesTransform.position.x[eid] = 0;Transform.position.y[eid] = 0;Transform.position.z[eid] = 0;Transform.scale.x[eid] = 1;Transform.scale.y[eid] = 1;Transform.scale.z[eid] = 1;// Set a display name (stored in EntityMetadataStore)setEntityDisplayName(eid, 'Player');
When removing an entity, Web Engine ensures all components and metadata are cleaned up properly. The EntityCleanupSystem handles this at the end of each frame.
import { removeEntity } from 'bitecs';import { clearEntityMetadata } from '@web-engine-dev/core';// Remove an entityremoveEntity(world, eid);// Clean up metadata (done automatically by EntityCleanupSystem)clearEntityMetadata(eid);// The entity ID is now recycled and can be reused
Deferred Cleanup
Web Engine uses deferred entity cleanup to avoid issues with systems still processing entities. When you call removeEntity, the entity is marked for removal but not immediately destroyed. The EntityCleanupSystem runs at the end of the frame to finalize cleanup.
bitECS automatically recycles entity IDs to prevent unbounded growth of internal arrays. When an entity is removed, its ID goes into a free list and will be reused for the next entity creation.
// Create first entityconst eid1 = addEntity(world); // ID: 0// Create second entityconst eid2 = addEntity(world); // ID: 1// Remove first entityremoveEntity(world, eid1);// Create third entity - reuses ID 0const eid3 = addEntity(world); // ID: 0 (recycled!)console.log(eid3 === eid1); // true
Stale Entity References
Because entity IDs are recycled, storing entity IDs long-term can be dangerous. If you hold a reference to entity 42, destroy it, then create a new entity, ID 42 might refer to a completely different object. Always validate entity references before use.
Queries let you find all entities with a specific set of components. Web Engine uses bitECS queries, which are automatically cached for O(1) performance after the first call.
import { defineQuery } from 'bitecs';import { Transform, RigidBody, Mesh } from '@web-engine-dev/core';// Define a query for all entities with Transform and RigidBodyconst physicsQuery = defineQuery([Transform, RigidBody]);// Execute the query (cached after first call)const entities = physicsQuery(world);// Iterate over resultsfor (let i = 0; i < entities.length; i++) {const eid = entities[i];// Access componentsconst mass = RigidBody.mass[eid];const x = Transform.position.x[eid];console.log(`Entity ${eid} at (${x}, ...) with mass ${mass}`);}
For more details on queries, see the Queries documentation.
Web Engine supports parent-child relationships between entities using the Parent and LocalTransform components. This enables scene graphs where child entities inherit their parent's transform.
import { addComponent } from 'bitecs';import { Transform, LocalTransform, Parent } from '@web-engine-dev/core';// Create parent entityconst parentEid = addEntity(world);addComponent(world, Transform, parentEid);Transform.position.x[parentEid] = 10.0;// Create child entityconst childEid = addEntity(world);addComponent(world, Transform, childEid);addComponent(world, LocalTransform, childEid);addComponent(world, Parent, childEid);// Set parent referenceParent.eid[childEid] = parentEid;// Set local transform (relative to parent)LocalTransform.position.x[childEid] = 5.0;// Child's world position will be 15.0 (parent 10.0 + local 5.0)
Transform — World-space transform (final calculated position)LocalTransform — Local-space transform (relative to parent)Parent — Stores parent entity IDSiblingIndex — Order among siblings (for editor)Transform Propagation
Web Engine uses WASM-accelerated transform propagation to update world transforms from local transforms efficiently. The WasmTransformSystem processes entire hierarchies in parallel.
Not all entity data fits in TypedArrays. For strings, objects, and complex data, Web Engine uses the EntityMetadataStore — a unified Map that stores non-primitive data.
import {setEntityDisplayName,getEntityDisplayName,setMetadata,getMetadata,} from '@web-engine-dev/core';// Set display namesetEntityDisplayName(eid, 'Player 1');// Get display nameconst name = getEntityDisplayName(eid); // "Player 1"// Store custom metadatasetMetadata(eid, 'playerInfo', {name: 'Alice',avatarId: 'avatar_001',});// Retrieve custom metadataconst info = getMetadata(eid, 'playerInfo');console.log(info.name); // "Alice"
name — Display name for editor and debuggingbillboard — Billboard widget metadatainteractable — Interaction prompt and actionplayerInfo — Player display infoinventory — Inventory items arrayanimationRuntime — Animation graph statelogicRuntime — Logic graph execution stateMemory Efficiency
EntityMetadataStore only allocates memory for entities that actually have metadata. Empty metadata objects are automatically cleaned up to prevent memory leaks.
Web Engine supports entity prefabs — reusable templates that can be instantiated multiple times with different configurations.
import { PrefabManager, Transform } from '@web-engine-dev/core';// Get the prefab managerconst prefabs = PrefabManager.getInstance();// Load a prefab definition from asset registryconst cratePrefab = await prefabs.load('assets/prefabs/crate.prefab');// Instantiate prefabs at different positionsconst crate1 = prefabs.instantiate(world, cratePrefab, {position: [0, 1, 0]});const crate2 = prefabs.instantiate(world, cratePrefab, {position: [5, 1, 0]});const crate3 = prefabs.instantiate(world, cratePrefab, {position: [10, 1, 0]});