Initialize the world
The world owns every entity, archetype table, query, system, resource, and
runtime registration. ecs_init() creates the active world and
ecs_fini() releases it.
The public API operates on that world. Component identifiers, entity handles, and persistent queries all refer to state owned by the same runtime.
Register components and create entities
ECS_COMPONENT declares a typed data component.
ECS_COMPONENT_REGISTER associates that type with a runtime
component identifier in the active world.
#include <siecs.h>
ECS_COMPONENT(Position, {
float x;
float y;
});
int main(void) {
ecs_init();
ECS_COMPONENT_REGISTER(Position);
ecs_entity_t entity = ecs_new();
ecs_set(entity, Position, { .x = 10.0f, .y = 20.0f });
Position *position = ecs_get(entity, Position);
position->x += 1.0f;
ecs_fini();
return 0;
} ecs_new() creates an entity handle. Setting a component adds
it to the entity when needed and writes the value into the destination
archetype column.
Define the required component access
Query terms describe both matching and access. In this query,
Position is writable and Velocity is read-only.
ecs_query_id_t moving = ecs_query({
.terms = { ecs_inout(Position), ecs_in(Velocity) },
}); The query caches every archetype table that satisfies those terms. Filter, exclude, and optional terms refine the same matching model.
Iterate contiguous component batches
An iterator walks the query's cached table set. Each successful step exposes one batch, its component columns, and the number of rows in that table.
ecs_iter_t it = ecs_query_iter(moving);
while (ecs_iter_next(&it)) {
Position *positions = ecs_field(&it, 0);
const Velocity *velocities = ecs_field(&it, 1);
for (uint32_t i = 0; i < it.count; i++) {
positions[i].x += velocities[i].x;
}
} Field order follows query-term order. The loop works directly on contiguous component arrays for the current archetype table.
Schedule the query as a system
A system stores a persistent query, a callback, and an execution phase.
ecs_progress() runs enabled systems in phase order.
ecs_system({
.query.terms = {
ecs_inout(Position),
ecs_in(Velocity),
},
.callback = Move,
.phase = EcsOnUpdate,
}); The same world can also register observers for events, resources for unique values, relations for entity targets, and modules for grouped imports.
Compile the runtime with the application
The standalone distribution contains siecs.h and
siecs.c. Add both files to the project, include the public
header, and compile the runtime with the application.
cc -std=c23 -I. main.c siecs.c -pthread -o my_app The C ABI is also the foundation for the typed C++ API, engine modules, native tools, and additional language integrations.