Introducing Craters: A TypeScript Game Framework

I’ve been building browser games on and off since I was a teenager — nothing serious, mostly messing around. At some point I kept running into the same problem: the raw Canvas API is fine, but the moment you want any structure in your game code, you end up writing the same boilerplate every time. The big frameworks felt like too much. I just wanted something lightweight I could actually understand.

So I built Craters.

What is it?

Craters is a modular game framework for HTML5, written in TypeScript. It started as a JavaScript project and I rewrote it from scratch in TypeScript — more on why in a later post.

The core of it is an Entity-Component-System (ECS) architecture. If you haven’t worked with ECS before, the short version is:

Concept What it does
Entity A unique ID — represents a “thing” in your game
Component Data (and sometimes behaviour) attached to an entity
System Logic that runs each tick on entities with specific components

ECS works well for games because it keeps things composable. You don’t end up with a Player extends Character extends MovableObject chain that breaks when you need something weird.

Getting started

npm install craters

Here’s a minimal setup using the current API:

import { EntityComponentSystem as ECS, RenderLoop } from 'craters';

class Position extends ECS.Component {
  constructor(public x: number, public y: number) { super(); }
}

class Velocity extends ECS.Component {
  constructor(public dx: number, public dy: number) { super(); }
}

class MovementSystem extends ECS.System {
  execute(delta: number) {
    const query = this.world.createQuery([Position, Velocity]);
    query.entities.forEach(entity => {
      const pos = entity.getComponent(Position);
      const vel = entity.getComponent(Velocity);
      pos.x += vel.dx * delta;
      pos.y += vel.dy * delta;
    });
  }
}

const world = new ECS.World();
world.registerSystem(new MovementSystem());

const player = world.createEntity();
player.addComponent(new Position(0, 0));
player.addComponent(new Velocity(1, 0));

new RenderLoop(delta => world.execute(delta));

That’s a working game loop with an entity that moves.

What’s in the box

Out of the box, Craters ships with:

  • Entity-Component-System core (World, Entity, System, Query)
  • WebGL sprite batching renderer + Canvas2D fallback
  • Collision detection (SAT — circles and convex polygons)
  • QuadTree for broad-phase spatial partitioning
  • Keyboard, mouse, and touch input
  • Web Audio API wrapper for sound
  • Tilemap support (Tiled JSON format)
  • Zero external runtime dependencies

Why TypeScript?

The short answer: TypeScript found real bugs in my code that I never would have caught otherwise. The longer answer is in the next post.

Source: github.com/john-swana/craters

comments powered by Disqus