Quick Start
Build a working ECS application in 5 minutes.
Overview
This guide walks you through creating a simple movement system. By the end, you'll understand the core workflow of bitECS.
1. Create a World
A world is the container for all ECS data. Start by creating one:
import { createWorld } from 'bitecs'
const world = createWorld()Worlds are independent - you can have multiple worlds running simultaneously.
2. Define Components
Components are data containers. Define them as objects with arrays:
// Position component (x, y coordinates)
const Position = {
x: [] as number[],
y: [] as number[],
}
// Velocity component (movement speed)
const Velocity = {
x: [] as number[],
y: [] as number[],
}This Structure of Arrays (SoA) format stores all x values together and all y values together, which is more cache-efficient than storing each entity's data as a separate object.
3. Add Entities
Entities are just numbers. Create them and attach components:
import { addEntity, addComponent } from 'bitecs'
// Create an entity
const entity = addEntity(world)
// Add components
addComponent(world, entity, Position)
addComponent(world, entity, Velocity)
// Set initial values
Position.x[entity] = 100
Position.y[entity] = 100
Velocity.x[entity] = 2
Velocity.y[entity] = 14. Query & Update
Query for entities with specific components and update their data:
import { query } from 'bitecs'
// Find all entities with Position AND Velocity
const movingEntities = query(world, [Position, Velocity])
// Update each entity
for (const eid of movingEntities) {
Position.x[eid] += Velocity.x[eid]
Position.y[eid] += Velocity.y[eid]
}5. Render & Loop
Add a simple render function and run everything in a game loop:
// Get canvas context
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// Render system - draw entities as circles
const renderSystem = () => {
ctx.fillStyle = '#161616'
ctx.fillRect(0, 0, canvas.width, canvas.height)
for (const eid of query(world, [Position])) {
ctx.fillStyle = '#0f62fe'
ctx.beginPath()
ctx.arc(Position.x[eid], Position.y[eid], 4, 0, Math.PI * 2)
ctx.fill()
}
}
// Game loop
const loop = () => {
movementSystem(world)
renderSystem()
requestAnimationFrame(loop)
}
loop()Complete Example
quickstart.ts
1import { createWorld, addEntity, addComponent, query } from 'bitecs'23// Components4const Position = { x: [] as number[], y: [] as number[] }5const Velocity = { x: [] as number[], y: [] as number[] }67// World8const world = createWorld()910// Spawn entities11for (let i = 0; i < 50; i++) {12 const eid = addEntity(world)13 addComponent(world, eid, Position)14 addComponent(world, eid, Velocity)15 Position.x[eid] = Math.random() * 40016 Position.y[eid] = Math.random() * 30017 Velocity.x[eid] = (Math.random() - 0.5) * 318 Velocity.y[eid] = (Math.random() - 0.5) * 319}2021// Movement system22const movementSystem = () => {23 for (const eid of query(world, [Position, Velocity])) {24 Position.x[eid] += Velocity.x[eid]25 Position.y[eid] += Velocity.y[eid]26 if (Position.x[eid] < 0 || Position.x[eid] > 400) Velocity.x[eid] *= -127 if (Position.y[eid] < 0 || Position.y[eid] > 300) Velocity.y[eid] *= -128 }29}3031// Render system32const canvas = document.querySelector('canvas')33const ctx = canvas.getContext('2d')3435const renderSystem = () => {36 ctx.fillStyle = '#161616'37 ctx.fillRect(0, 0, 400, 300)38 ctx.fillStyle = '#0f62fe'39 for (const eid of query(world, [Position])) {40 ctx.beginPath()41 ctx.arc(Position.x[eid], Position.y[eid], 4, 0, Math.PI * 2)42 ctx.fill()43 }44}4546// Game loop47const loop = () => {48 movementSystem()49 renderSystem()50 requestAnimationFrame(loop)51}5253loop()Next Steps
Now that you understand the basics:
- Learn about Worlds and custom contexts
- Explore Query operators like Or and Not
- Add Relationships for parent-child hierarchies
- Use Observers to react to changes