Efe Anidi
Simulation & agents

bitECS

A minimal, data-oriented Entity Component System for TypeScript and JavaScript, built for large numbers of entities.

Best for
Simulating many entities in the browser
Pricing
Free (open source)
Licence
MPL-2.0
Site
bitecs.dev ↗

What it is

bitECS is a small Entity Component System library for TypeScript and JavaScript, written by Nathaniel Martin (NateTheGreatt on GitHub). It describes itself as flexible, minimal and data-oriented, and it is deliberately unopinionated: entities are plain numeric IDs, components are whatever data structures you like, and systems are ordinary functions that query the world for entities with a given set of components.

The idea behind data-oriented design is that you store data by field rather than by object. Instead of a thousand Enemy objects each holding an x and y, you keep one Float32Array of x positions and one of y positions, indexed by entity ID. Loops over those arrays are cache-friendly and fast. bitECS lets you use that structure-of-arrays layout, or plain arrays of objects where convenience matters more.

The current release is 0.4.0 (December 2025), which reworked the API compared with the widely used 0.3 series. It has zero dependencies, is about 5 kB minified and gzipped, and includes serialisation, relationships between entities and support for multithreading. It has been used in Mozilla Hubs and Third Room.

Where it fits in AI-driven worlds

Simulations get interesting when there are a lot of things in them: crowds, herds, traffic, particles with behaviour, colonies of agents competing for resources. Once you are past a few thousand entities in a browser, the object-per-agent style starts to cost you, and that is where bitECS earns its place.

ECS suits AI-driven worlds for a second reason: it separates data from behaviour cleanly. Perception, decision-making and movement become separate systems that run in order each tick. You can swap a hand-written decision system for one that calls a trained model, or run the expensive one less often, without touching the rest. The world’s state is just arrays, which also makes it straightforward to snapshot, serialise, send over a network or feed into analysis.

Strengths

  • Performance. Typed-array components and simple queries handle large entity counts well, and the project documents a multithreading approach for spreading work across workers.
  • Small and dependency-free. Easy to drop into any project, including alongside three.js or another renderer.
  • Bring your own data. Components can be structure-of-arrays, arrays of objects or plain arrays, so you choose the trade-off per component.
  • Relationships. 0.4 adds relations between entities, useful for parent-child links, targeting or ownership.
  • Serialisation included. Helpful for save games, replays and networking.
  • TypeScript-first. Types come with the package.

Limits

  • You build the framework. There is no scheduler, no rendering, no editor and no scene format. bitECS gives you the core and expects you to architect the rest.
  • API changes between versions. Much existing tutorial code and many examples use the 0.3 API (defineComponent, Types.f32, defineQuery), which differs from 0.4. Check which version any sample targets.
  • Manual sizing. If you preallocate typed arrays, you have to choose a capacity and handle running out.
  • Steeper mental model. If you are used to classes with methods, thinking in components and systems takes some adjustment.
  • Licence. MPL-2.0 is a file-level copyleft. You can use it in closed-source projects, but changes to bitECS’s own files must be shared under the same licence.

Getting started

npm i bitecs

A movement system using the 0.4 API with structure-of-arrays components. Tested against bitecs 0.4.0:

import { createWorld, addEntity, addComponent, query } from 'bitecs';

const MAX = 10_000;
const Position = { x: new Float32Array(MAX), y: new Float32Array(MAX) };
const Velocity = { x: new Float32Array(MAX), y: new Float32Array(MAX) };

const world = createWorld();

const eid = addEntity(world);
addComponent(world, eid, Position);
addComponent(world, eid, Velocity);
Velocity.x[eid] = 1;
Velocity.y[eid] = 0.5;

function movementSystem(world, dt: number) {
  for (const e of query(world, [Position, Velocity])) {
    Position.x[e] += Velocity.x[e] * dt;
    Position.y[e] += Velocity.y[e] * dt;
  }
}

movementSystem(world, 1 / 60);

The README also shows registering components on the world with createWorld({ components: { ... } }), which keeps everything in one place if you prefer.

Verdict

bitECS is a good foundation when a browser simulation needs to handle many entities and you are happy to design the surrounding architecture yourself. It is fast, small and flexible. If you want a ready-made engine, or have only a few dozen agents, it is more structure than you need.

More in Simulation & agents