Newer
Older

ECS, ECS or ECS? What's the difference?

Date: 2026-10-20 22:50
Tags: programming

I think there's some confusion about the precise meaning of "ECS" or "Entity Component System" as applied in game design and potentially other applications. Even the first two sentences of the Wikipedia article don't agree on whether it's a countable or uncountable noun:

Entity component system (ECS) is a software architectural pattern. An ECS consists [...]
You wouldn't say "An OOP consists of ..."

So here's my taxonomy of the several meanings of "ECS" and other things it's easily confused with. Although these things are quite strongly overlapping and intertwined, I hope to tease them apart a little.

Entity/Component/System (architecture)

Software can be written in an architectural style with entities, components, and systems — whether they are called that, or not.

ECS says that your game world (this is an architecture specific to games) is made of entities, but the entities in the game world aren't necessarily reified in the game's code (there is no single Entity structure). Instead, an entity results from the combination of different aspects, such as its 3D model, health bar, and physics box. It likewise says that the game code can be divided into different modules, or systems, such as rendering, combat, and physics.

If you've ever used a third-party physics library for your game, you've probably had to create a "physics object" corresponding to each game object that has physics. The physics library takes care of simulating the physics for all the physics objects, and once per frame, you copy their positions and rotation back to the game objects for rendering — quite a weak connection. At best, your struct Entity contains a pointer to a physics object.

This decoupling and modular self-containment is exemplary ECS style. The physics object is a component and the physics library is a system. You might also have it in other parts of the game such as networking, though I think physics is the most common.

ECS architectural style doesn't imply a certain way of storing data in memory. There are many different data structures that implement this style.

Entity/Component System

When using ECS architecture, you might want to use a third-party (or first-party) software module to manage your entities and components. This is a system that manages your entities and components—but it's a different meaning of "system" from the one that defines the architecture style! The word "ECS" also frequently means this.

EnTT is one library that identifies itself as "an entity-component system". By looking at the examples, it's clear that it manages entities and components, but it doesn't manage systems, so it is an entity/component system or (if you hate clear communication) "an ECS". We could instead call it an "EC library" or an "EC framework" to reduce ambiguity.

Some would argue that the systems in ECS architecture shouldn't be managed by a library, so every EC library is also an ECS library, as the S part is a no-op.

The "system" in "entity/component system" is emphatically not the same "system" in "entity/component/system architecture".

You could also call it an "entity component system", as it is a system that manages components of entities (entity components) but that is the exact same architecture so it's a distinction without a difference. You can't have entity components unless you also have entities. A system that manages entity components necessarily manages entities as well.

Entity/Component/System system

Now it really gets confusing. Some EC systems also have facilities for managing systems, which makes them "entity/component/system systems" or "ECS systems". The word "ECS" sometimes means one of these.

For example, EntityX is similar in concept to EnTT, but also manages systems. It calls itself "an Entity Component System" and doesn't even try to clarify what that means at all.


Structure of Arrays (SoA)

SoA is an implementation pattern that looks about like this:
struct Game {
    vector<vec2> position;
    vector<vec2> velocity;
    vector<float> health;
};
instead of the more common Array of Structures:
struct Entity {
	vec2 position;
	vec2 velocity;
	float health;
};
struct Game {
	vector<Entity> entities;
};
SoA more efficiently uses the CPU cache when performing the same operation on many entities (such as position += velocity*timestep;).

It gets confused with ECS (any of them) because the possibility of using SoA is one of the benefits of ECS architecture. While OOP architecture typically forces you to have an array of pointers to structures, ECS architecture admits more implementation variety, including SoA.

However, they are not the same thing. The example above is SoA, but it's not ECS because there are no components. The following "megastruct design" breaks up the entity into components, but doesn't use SoA:

struct Motion {
    vec2 position;
    vec2 velocity;
};
struct Combat {
    float health;
};

struct Entity {
    optional<Motion> motion;
    optional<Combat> combat;
};
struct Game {
    vector<Entity> entities;
};

Data-Oriented Design (DoD)

Data-Oriented Design is often confused with SoA, because in practice, a lot of it boils down to using SoA and few big loops.

However, DoD is a higher layer of abstraction. DoD is the practice of thinking about your program in terms of the data that flows through it, rather than, say, world modeling or code structure. It happens that on modern CPUs (and even in 2009, when the term came about) running video games, the efficient way to process data is usually with big loops over homogeneous arrays with small elements, but this is not always the case.

On processors from the 80s and 90s, for instance, linked lists were quite efficient. These processors had no caches or data prefetchers— every byte of memory access cost the same amount. A linked list allows you to jump instantly to the next item without extra lookups. When you're writing code for such an old processor, or even a modern tiny microcontroller with no cache, DoD thinking may lead you to use a lot of linked lists.

And on giant server clusters, DoD thinking may lead you towards MapReduce.

Here are some other illustrative examples of data-oriented thinking are:

  • Thinking about a game engine as a transformation from an old complete game state to a new complete game state, e.g. foreach(Entity e) e.pos += e.vel * timestep; rather than thinking of each entity moving itself separately.
    This can avoid problems caused by evaluation order. This is also the S in ECS architecture.
  • SoA, as discussed previously, which allows you to do that transformation without polluting the CPU cache with the other fields of each entity.
  • Column-oriented databases, which do the same thing at scale.
  • Network switches which are hard-wired for certain exact header stacks (such as Ethernet+VLAN+MPLS+IPv4). Usually only the outer layers, which are fixed, are used for directing the packet, so a mismatch of inner headers doesn't cause any correctness problem, but since those headers can be used for features like traffic shaping, packets with unexpected header combinations will fall back to a default classification.
  • SDF ray-marching shaders used in the demoscene, which directly aim to get the pixels on the screen, skipping things like mesh geometry.
  • Factorio uses an "electricity node" each time an entity connects to the electricity grid. Although Factorio is mostly AoS-based, the electricity nodes are stored outside the main AoS, all together in a consecutive block of memory, so the electricity grid iterates over these. This is a limited use of ECS/SoA.
  • "Archetypal ECS" goes even further than SoA by grouping together entities with the same set of components, so there are no gaps. For example it would create one SoA for entities that have position, velocity, and health; and another SoA for entities that only have position and velocity, avoiding gaps in the health array, and avoiding any need for the combat system to skip over entities that don't have health.
DoD doesn't imply SoA. It may be that it's faster to run the position/velocity loop with positions and velocities interleaved with each other, or in two separate arrays, and data-oriented thinking would implore you to actually benchmark both and see which is faster.