ECS guide
Entity-component system
Create local schemas, query object-shaped components, and keep system composition in the caller.
What is an ECS?
An entity-component system (ECS) organizes game state by separating identity, data, and behavior. An entity is a numeric ID, components are small pieces of data such as position or health, and systems are ordinary functions that query entities with the data they need and update them. This makes it easy to process many similar things—such as every projectile or enemy—without putting every behavior in one large game-object class.
Play ECS Arena
Play ECS Arena — a small WebGPU game that makes the ECS lifecycle visible.
Live example
Run a typed world
Create an entity to start the world
World snapshot
{
"format_version": 1,
"entity_policy": "monotonic",
"entities": [],
"components": {
"position": []
}
}What this demonstrates
Component tokens belong to a local schema, and each world owns its membership and component storage. Queries expose only the tokens they request. The example uses detached query snapshots for the reactive list and a caller-controlled update function for movement.
Public types
define_component<Value>()create_world(schema, options?)EntityPolicyandWorldOptionsWorld<Schema>Query<Schema, Included>QueryMatch<Schema, Included>WorldSnapshot<Schema>
Minimum viable example
// Import the world factory and component definition helper.
import { create_world, define_component } from "squander";
// Describe the data stored by each position component.
type Position = { x: number; y: number };
// Create a typed token that identifies the position component.
const position = define_component<Position>({ name: "position" });
// Create a world whose local schema contains the position token.
const world = create_world({ position });
// Allocate a cheap numeric entity ID.
const entity = world.create();
// Attach the initial position data to the entity.
world.add(entity, position, { x: 0, y: 0 });
// Require position before iterating matching entities.
world.query().with(position).each(({ read, write }) => {
// Read the current component value, then write the updated value.
const { y } = read(position);
write(position).x += y;
});
// Export a storage-independent snapshot of the world.
const snapshot = world.export();
// Restore the snapshot, including its entity IDs and component values.
world.import(snapshot); Caller-owned systems
Systems are ordinary application modules. The ECS supplies data and queries, while a scene or game loop chooses which lifecycle hooks to call and in what order.
const movement = {
enter: () => {},
update: () => {
world.query().with(position).each(({ read, write }) => {
write(position).x += read(position).y;
});
},
draw: () => {},
};
// The caller owns scheduling and may expose only the hooks it needs.
const scene = {
enter: movement.enter,
update: movement.update,
draw: movement.draw,
}; Scope and limitations
The ECS uses object-shaped components and dense sparse-set columns today. Structural changes from a query match are deferred until
iteration finishes. Monotonic IDs are the default; generational IDs are available through { entity_policy: "generational" }. Snapshot values must be structured-cloneable. The ECS does not provide a scheduler or system registry. JSON and binary serialization,
migrations, typed columns, and archetype storage remain application or later optimization seams.