Documentation Index
Fetch the complete documentation index at: https://mintlify.com/VKSFY/keel/llms.txt
Use this file to discover all available pages before exploring further.
World is the central hub of Keel’s Entity-Component-System architecture. It stores every live entity, organises components into cache-friendly archetype tables, routes events through a per-frame bus, holds singleton resources, and drives the Scheduler. You never construct a World directly in normal usage — App creates one and exposes it as app.world.
NULL_ENTITY = 0 is the sentinel value for “no entity”. A World always starts entity IDs at 1, so any ID equal to 0 is guaranteed to be absent from all queries and inspection calls.Entity Lifecycle (deferred)
Structural changes — spawning, despawning, and adding or removing components — are buffered in aCommandBuffer and only become visible to queries after the next flush(). The main loop calls flush() automatically at the end of every simulation tick.
world.spawn(*components) -> int
Allocate a fresh entity ID and queue a deferred spawn with the given components. Each component type may appear at most once; passing duplicates raises ValueError.
One or more
@keel.component-decorated instances. All become columns in the entity’s archetype.The new entity ID (always ≥ 1). The entity is not yet visible to queries until
flush().world.despawn(entity: int)
Queue a deferred despawn. The entity is removed from its archetype, its row is swap-removed, and its ID is returned to the free list for recycling — all after the next flush().
Entity ID to despawn. Despawning an already-dead entity is silently ignored after flush.
world.add_component(entity: int, component: Any)
Queue a deferred component addition. If the entity already owns a component of this type, the existing value is overwritten in place (no archetype migration). Otherwise, the entity migrates to a new archetype that includes the new type.
Target entity ID.
A
@keel.component instance to attach.world.remove_component(entity: int, component_type: type)
Queue a deferred component removal. The entity migrates to a new archetype that excludes the given type after flush(). Removing a component the entity does not own is silently ignored.
Target entity ID.
The
@keel.component class to remove.world.flush()
Apply every queued CommandBuffer command in the order it was buffered. Commands processed:
spawn— places entity into the correct archetype tabledespawn— swap-removes entity row, recycles IDadd_component— migrates entity to a superset archetype or overwrites in placeremove_component— migrates entity to a subset archetype
flush() automatically at the end of each simulation tick. Call it manually when you need an entity to be immediately queryable within the same system.
Entity Inspection (synchronous)
These methods read the archetype tables directly and return results immediately — no buffering.world.is_alive(entity: int) -> bool
Return True if entity has been flushed and currently resides in an archetype.
world.has_component(entity: int, component_type: type) -> bool
Return True if entity is alive and its archetype includes component_type.
world.get(entity: int, component_type: type) -> dict | None
Return the entity’s component fields as a plain Python dict of scalar values, or None if the entity is absent or does not own the component. Numpy scalars are converted via .item() so you get int / float / bool — not numpy.int64.
Entity ID to inspect.
The component class whose fields to read.
Field name → Python scalar mapping, or
None if the entity or component is absent.world.set(entity: int, component_type: type, **fields) -> bool
Write one or more component fields in place. For numpy-backed components the underlying structured array is updated directly (no allocation). Returns False if the entity or component is absent, True on success. Raises ValueError if any keyword names a field that does not exist on the component.
Entity ID to modify.
The component class whose fields to write.
Keyword arguments mapping field names to new values.
True on success, False if the entity or component is not present.world.get_component(entity: int, component_type: type) -> Any
Reconstruct and return a full component instance (not a dict). Useful when you need the original dataclass object rather than a field dict. Returns None if absent.
Entity ID to inspect.
The component class to reconstruct.
A fresh instance of
component_type populated with the entity’s current field values, or None.world.location_of(entity: int) -> tuple[Archetype, int] | None
Return the low-level (archetype, row_index) location of an entity, or None if not alive. This is an advanced escape hatch for tools and custom serialisers that need direct column access.
The archetype table and integer row index, or
None.Queries
Keel queries scan archetype tables — not entity lists — so they are efficient even with thousands of entities.world.query(*args) -> QueryResult
Build a query over one or more component types, Without[C] exclusion markers, and Optional[D] optional-column markers. Returns a QueryResult that yields one tuple of column views per matching archetype (not per entity).
One or more
@keel.component classes, keel.Without[C] markers, or keel.Optional[D] markers. At least one required component type must be present.Iterable that yields
tuple[column, ...] per matching archetype. Required columns are always numpy arrays or Python lists; optional columns are None when the archetype lacks the type.QueryResult also exposes helper methods:
.entities()— iterator over every matched entity ID.count()— total number of matched entities.archetypes()— list of matchedArchetypeobjects
world.query_one(component_type: type) -> dict | None
Return the first entity’s component fields as plain Python scalars. Intended for singleton components that should appear on exactly one entity (e.g. GameState, Camera2D). Returns None if no entity owns the component.
The singleton component class to look up.
Field name → Python scalar dict from the first matching entity’s row, or
None.Query Markers
Without[C]
Excludes archetypes that contain component type
C. Use keel.Without[Frozen] to skip frozen entities in a movement system.Optional[D]
Includes the column for
D if present in the archetype, otherwise yields None for that slot. Lets one query handle entities with and without an optional component.NULL_ENTITY
NULL_ENTITY is never alive. Use it to initialise entity-reference fields.
Resources
Resources are singleton objects stored in theWorld and injected into systems by type annotation. Common uses: renderer state, audio engine, physics world, configuration.
world.insert_resource(resource, type_=None)
Register resource as a singleton keyed by its type (or by type_ if provided).
The singleton object to register.
Key override. Useful for registering a subclass instance retrievable by its base class.
world.get_resource(type_) -> Any
Return the registered resource of the given type, or None if not found.
world.has_resource(type_) -> bool
Return True if a resource of the given type is currently registered.
world.remove_resource(type_) -> Any
Remove and return the resource registered under type_, or None if absent.
Events
Keel’s event bus clears all queues at the start of every visual frame, so events emitted by a system during tick N are readable by later systems in tick N but gone by tick N+1.world.emit(event_instance)
Queue an event for all systems to read this frame.
An instance of a class decorated with
@keel.event.world.read_events(event_type) -> Iterator
Iterate over all events of the given type emitted this frame.
The
@keel.event-decorated class to filter on.Iterator over every queued instance of
event_type for this frame.Systems
world.system(phase, after=None)
Decorator to register a system function in the given phase. Equivalent to @app.system(phase) — both target the same shared Scheduler.
Scheduler.register for details on after ordering and resource injection.
world.tick(dt: float)
Run one complete frame manually: clear events, invoke all systems in phase order, then flush commands. Used in tests and headless simulations — App.run() calls the loop’s FixedStepDriver instead.
The simulated delta time to pass to every system.
