Entity Component System

An archetype ECS for C and C++.

SIECS groups entities by component set, stores components in contiguous columns, and runs systems over cached table queries.

C23 runtime C++ typed API Standalone distribution Zero runtime dependencies MIT license

The runtime is built around five concepts.

Each concept has a distinct role in storage or execution. Together they define how data moves through the world.

World

The world owns entity records, component storage, query state, systems, resources, and runtime metadata.

ecs_init() / ecs_fini()

Entity

An entity is a handle associated with a component set. Adding or removing components changes the entity's archetype.

ecs_entity_t / ecs_new()

Archetype

Entities with the same component set share a table. Every data component occupies a contiguous column inside that table.

[Position] [Velocity] [Entity]

Query

A query describes required access and caches every archetype table that satisfies its terms.

ecs_query({ .terms = { ... } })

System

A system binds a persistent query to a callback and an execution phase. The world runs enabled systems through ecs_progress().

query + callback + phase

From query terms to component batches.

Query matching and data iteration are separate steps. The query stores compatible tables, while the iterator exposes their component columns.

Describe access Write Position, read Velocity, exclude Disabled
Cache matches Store compatible archetype tables
Iterate data Expose component columns and batch length

Value updates stay in place. Writing an existing component updates its current archetype column.

Composition changes move entities. Adding or removing a component transfers the entity to the matching archetype.

Tags only affect matching. A zero-sized component changes the archetype identity without allocating a data column.

The same runtime from C and C++.

C exposes query terms and batch iterators explicitly. C++ derives component access from callback parameter types and calls the same runtime.

Compare the APIs
ecs_query_id_t moving = ecs_query({
    .terms = {
        ecs_inout(Position),
        ecs_in(Velocity),
    },
});

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;
        positions[i].y += velocities[i].y;
    }
}

Composition stays available at runtime.

Storage, execution, relationships, and metadata use the same world model. Tools can inspect the same information that application code uses.

Storage and execution

Archetype tables hold component columns. Cached queries select tables, iterators expose batches, and systems schedule callbacks by phase.

query / iterator / system / phase

Runtime composition

Resources store world-level values. Observers react to events. Relations express hierarchy, entity targets, and inheritance. Modules group related registrations behind one import.

resource / observer / relation / module

Runtime metadata

Reflection describes component fields. JSON support serializes reflected values, and the optional REST explorer exposes schemas and entities to external tools.

reflection / JSON / REST explorer

Add the runtime to the application.

Copy siecs.h and siecs.c from the standalone distribution. C and C++ applications include the same public header and link the same C runtime.

cc -std=c23 -I. main.c siecs.c -pthread -o my_app

Technical model.

How are components stored?

Each archetype table represents one exact component set. Data components occupy separate contiguous columns, while tags contribute only to the table identity.

What does a query cache?

A persistent query stores the archetype tables that satisfy its terms. An iterator walks that table set and resolves the requested columns.

How are systems executed?

A system owns a persistent query, a callback, and an execution phase. ecs_progress() evaluates enabled systems in phase order.

How does tooling access world data?

Reflection records component schemas, JSON serializes reflected values, and the optional REST explorer provides schema and entity endpoints.

Build the first world.

Register components, create an entity, define a system, and progress the runtime.

Read the quick start