@glom/ecs v1.0.0

Introduction

Glom is an Entity-Component System written in TypeScript. It aims to be a fast, networkable, and testable foundation for games with dynamic behavior.

Features

Systems are just functions, so they’re familiar to write and test.

const movement = (q: All<Pos, Write<Vel>>) => {
  for (const [p, v] of q) {
    p.x += v.x
    p.y += v.y
  }
}

// in a test
const pos = {x: 0, y: 1}
const vel = {x: 1, y: 1}

movement([[pos, vel]])

expect(pos).toEqual({x: 1, y: 2})

Entity relationships help you model hierarchies and graphs.

const Contact = defineRelation()

const collide = (query: Join<All<Entity, Pos>, All<Entity, Pos>>, spawn: Spawn<typeof Contact>) => {
  for (const [a, aPos, b, bPos] of query) {
    if (a !== b && intersects(aPos, bPos)) spawn(Contact(b))
  }
}

const damage = (query: All<Write<Health>, Contact>, poof: Despawn) => {
  for (const [health, contact] of query) {
    health.value -= 10
    poof(contact)
  }
}

Networking utilities are components and systems you can mix-and-match to replicate worlds.

// built-in systems for reconciliation
addSystem(schedule, glom.performRollback)
addSystem(schedule, glom.applyRemoteTransactions)
addSystem(schedule, glom.advanceWorldTick)

// tag entities for sync
addComponent(world, player, Replicated)

// or within a system:
const syncSystem = (q: All<Entity>, add: Add<typeof Replicated>) => {
  for (const [e] of q) add(e)
}

The optimizer is a TypeScript transformer that injects system dependencies and inlines query loops.

// @glom/transformer rewrites:
for (const [pos, vel] of query) { ... }

// into fast while loops:
let i = 0
while (i < count) {
  const pos = posStore[i]
  // ...
}

Getting Started

Glom comes with many other neat things worth checking out, most of which are documented here. And you can hop straight to the Getting Started guide to start building, or browse common Recipes for query patterns.