Menu

Overview

Relevant source files

Purpose and Scope

This page introduces bitECS as an Entity Component System (ECS) library for TypeScript and JavaScript. It covers the library's design philosophy, key features, module organization, and high-level architecture. For installation and first steps, see Getting Started. For detailed explanations of ECS concepts, see Core Concepts. For advanced capabilities, see Advanced Features.

Sources: README.md1-26 docs/Intro.md1-13

What is bitECS?

bitECS is a flexible, minimal, data-oriented Entity Component System library for TypeScript and JavaScript. Unlike prescriptive ECS frameworks, bitECS provides low-level primitives that developers compose into custom architectures. The library achieves high performance through data-oriented design principles while maintaining a small footprint (~5kb minzipped).

The library implements the ECS architectural pattern where:

  • Entities are numeric identifiers managed by an EntityIndex
  • Components are arbitrary JavaScript references (arrays, objects, TypedArrays) that store data
  • Queries use bitmask operations for O(1) entity filtering
  • Systems are user-defined functions that process query results

Sources: README.md24-26 package.json4 docs/Intro.md3-8

Core Design Philosophy

Data-Oriented Design

bitECS embraces data-oriented design by separating data layout from API design. Component stores can be any JavaScript reference, allowing developers to optimize memory layout per use case. The library recommends Structure of Arrays (SoA) format for cache-friendly iteration but does not enforce it.

Minimal API Surface

The library provides only essential operations: createWorld, addEntity, addComponent, removeComponent, query. Higher-level abstractions like component definitions and system registration are left to user code. This minimalism keeps the library lightweight and prevents framework lock-in.

Flexibility Over Convention

bitECS avoids imposing architectural decisions. There is no formal concept of "systems"—only queries and user functions. Component stores live wherever developers choose. Multiple worlds can share or isolate data. This flexibility enables integration into existing codebases and supports diverse use cases from games to simulations.

Sources: docs/Intro.md3-13 README.md30-38

Key Features

FeatureDescription
Simple APIDeclarative functions for entities, components, and queries
Lightweight~5kb minzipped with zero runtime dependencies
Powerful QueryingBitmask-based filtering with And, Or, Not operators and hierarchy support
Relational ModelingEntity relationships via Pair components with wildcard querying
SerializationMultiple strategies (SoA, AoS, Snapshot, Observer) for network sync and persistence
Thread-FriendlySharedArrayBuffer support for concurrent access
TypeScript SupportFull type definitions with generic World types

Sources: README.md30-38 package.json1-78

Module Organization

Module Structure Explained

bitECS distributes functionality across three independent modules:

Core Module (bitecs): The main ECS implementation exported from dist/core/index containing all fundamental operations. This module has no external dependencies and provides the complete ECS foundation.

Serialization Module (bitecs/serialization): Optional serialization strategies for network synchronization and persistence, exported from dist/serialization/index Depends on the core module but can be excluded if serialization is not needed.

Legacy Module (bitecs/legacy): Backward-compatible API from earlier versions, exported from dist/legacy/index Provides higher-level abstractions like defineComponent that wrap core functionality with Observer and SoA serialization.

Sources: package.json10-35 package.json48-72

High-Level Architecture

Architecture Explained

The World object serves as the central coordinator, internally maintaining:

  • EntityIndex: Sparse set structure for O(1) entity lifecycle operations (src/core/EntityIndex.ts)
  • Component Registry: Maps component references to bitflags and generation IDs (src/core/Component.ts)
  • Entity Masks: Uint32Array bitmasks per component generation for fast query evaluation (src/core/Query.ts)
  • Query Cache: Hash-based cache of compiled queries with bitmask metadata (src/core/Query.ts)

Entity operations (addEntity, removeEntity) modify the EntityIndex and update all active queries. Component operations (addComponent, removeComponent) update entity masks via bitwise OR/AND operations, triggering query re-evaluation. The query function performs bitwise operations on entity masks to filter matching entities in O(1) time per entity.

Relations extend the component system by creating Pair components with metadata symbols ($relation, $pairTarget). The hierarchy system tracks entity depths in typed arrays for topological query ordering.

Sources: src/core/World.ts src/core/EntityIndex.ts src/core/Component.ts src/core/Query.ts

Core Data Flow

Data Flow Explained

  1. Entity Creation: addEntity allocates an ID from the EntityIndex sparse set and registers it in the world's entityComponents map
  2. Component Addition: addComponent registers the component (if new), assigns a bitflag, and updates the entity's bitmask via bitwise OR
  3. Query Evaluation: query retrieves or compiles a cached query with bitmasks, then iterates entity masks performing bitwise AND/OR/NOT operations
  4. Query Updates: When components change, queryCheckEntity re-evaluates affected queries and updates their result sets via queryAddEntity or queryRemoveEntity

This architecture achieves O(1) query evaluation per entity through bitmask operations, with query results cached in SparseSet structures for fast iteration.

Sources: src/core/Entity.ts src/core/Component.ts src/core/Query.ts

System Integration Points

Integration Points

  • Core Layer: The World, Entity, Component, and Query APIs form the foundation. All operations flow through these primitives
  • Relation System: Builds on components by creating Pair components with metadata. See Relations and Hierarchy
  • Hierarchy System: Extends relations with depth tracking for topological queries. See Relations and Hierarchy
  • Observable System: Provides reactive hooks for component and query changes. See Observers and Reactivity
  • Serialization Layer: Multiple strategies for persisting and syncing world state. See Serialization Overview
  • Legacy API: Higher-level abstractions wrapping Observer + SoA patterns. See Legacy API

Sources: src/core/index.ts src/serialization/index.ts src/legacy/index.ts

Performance Characteristics

OperationComplexityImplementation
addEntityO(1)Sparse set allocation from EntityIndex.dense
removeEntityO(1)Sparse set swap-remove from EntityIndex.dense
addComponentO(Q)Bitwise OR on mask, re-evaluate Q active queries
removeComponentO(Q)Bitwise AND NOT on mask, re-evaluate Q queries
hasComponentO(1)Check entityComponents Set membership
query (cached)O(N)Iterate N entities, bitwise AND/OR/NOT per entity
query (compilation)O(C)Hash C components, generate bitmasks
Entity iterationO(N)Dense array iteration, cache-friendly

Performance Notes:

  • Bitmask Operations: Query evaluation uses bitwise operations on Uint32Array bitmasks, enabling branch-free filtering
  • Query Caching: Compiled queries are cached by hash, eliminating re-compilation overhead
  • Sparse Sets: EntityIndex and query result sets use sparse set structures for O(1) add/remove with dense iteration
  • Component Generation: The library uses component generations to support up to 32 components per generation, automatically creating new generations as needed
  • Memory Layout: Component data layout is user-controlled. SoA format provides optimal cache locality for iteration

Sources: src/core/EntityIndex.ts src/core/Query.ts src/core/Component.ts

Thread Safety and Concurrency

bitECS supports concurrent access patterns through:

  • Immutable Entity IDs: Entity IDs are numeric values that can be safely shared across threads
  • SharedArrayBuffer Support: Component stores can use SharedArrayBuffer-backed TypedArrays for lock-free reads
  • Independent Worlds: Multiple world instances with shared EntityIndex enable parallel processing
  • Atomic Operations: User code can leverage JavaScript atomics for synchronized writes

For multithreading details, see Multithreading.

Sources: README.md37

TypeScript Support

The library provides full TypeScript definitions with generic types:

  • Generic World Type: World<T> preserves custom context types passed to createWorld
  • Type-Safe Components: Component references maintain their types through the API
  • Query Type Inference: Query results preserve entity ID types
  • Branded Types: Serialization uses symbol-branded types ($u8, $f32, etc.) for type safety

For TypeScript usage patterns, see TypeScript Support.

Sources: package.json12 dist/core/index.d.ts

When to Use bitECS

Ideal Use Cases:

  • Games requiring high entity counts (1M+ entities)
  • Simulations with complex relational data
  • Applications needing network synchronization
  • Projects requiring multithreading
  • Systems where memory layout optimization matters
  • Codebases needing ECS without framework lock-in

Considerations:

  • Requires manual component store management
  • No built-in system scheduling or execution order
  • Query operators limited to AND/OR/NOT logic
  • Serialization requires explicit setup

Production Usage:

  • Enchantment Engine
  • Third Room
  • Mozilla Hubs

Sources: README.md156-161

Next Steps