arcade2d

Guide

arcade2d games are assembled in a fixed order: a Game owns the canvas and loop, a World holds your objects, prefabs are reusable blueprints, and components are the behaviour you compose onto objects. This guide walks that scaffolding top to bottom — terse on purpose. Each step links into the API reference for the full detail.

1. Setting up Game

Everything hangs off a single Game. Game.bootstrap is async — it spins up the renderer, sizes the canvas, and starts the frame loop — so await it before doing anything else. The returned instance also owns game.assets and game.audio.

import { Game } from '@arcade2d/engine';

const game = await Game.bootstrap({
  backgroundColour: 0x1099bb,
  canvas: { fill: 'window' }, // or { width: 960, height: 640 }
  debug: true, // exposes the instance as window.game in the console
});

2. Creating a World

A World holds your objects and runs the per-frame update loop the Game drives. createWorld builds one and makes it active.

const world = game.createWorld();

World-level systems — things that act on the whole world rather than one object, like physics — are registered up front through the components factory. The keys you give them are how you look them up later.

import { PhysicsWorld } from '@arcade2d/engine';

const world = game.createWorld({
  components: (world) => ({
    physics: () => new PhysicsWorld(world, { gravity: { x: 0, y: 980 } }),
  }),
});

3. Setting up prefabs

A Prefab is a reusable blueprint for a world object: a name, optional tags, and a map of component factories. Define it once, spawn it many times. Each factory is handed a context — { object, world, assets } — so a component can wire itself to its host object and pull from the asset library.

import { Prefab, PolygonGraphics } from '@arcade2d/engine';
import { PlayerController } from './player.controller';

export const PlayerPrefab = new Prefab({
  name: 'player',
  tags: ['player'],
  components: {
    controller: ({ object }) => new PlayerController(object),
    graphics: ({ object }) =>
      PolygonGraphics.asRectangle(object, 50, 50, 0xffffff),
  },
});

Hand a PrefabRegistry to createWorld if you want to spawn prefabs by name instead of by import.

4. Understanding components

Components are where behaviour lives. There are two tiers:

Override only the lifecycle hooks you need; the world calls them each frame. this.host is the owning object, and this.world / this.game reach up the tree. Release anything you allocate in onDestroy.

import { AbstractWorldObjectComponent, WorldTimer } from '@arcade2d/engine';
import type { WorldUpdate } from '@arcade2d/engine';

export class PlayerController extends AbstractWorldObjectComponent {
  public override onUpdate(update: WorldUpdate): void {
    const keyboard = this.game.getKeyboardState();
    const mouse = this.world.getMouseState();
    const angle = this.host.position.angleTo(mouse.position);

    if (keyboard.isDown('KeyW')) {
      this.host.position.moveInDirection(angle, 0.08 * update.deltaMilliseconds);
    }

    this.host.rotation = angle;
  }
}

5. Spawning world objects

With a prefab defined, createFromPrefab instantiates its components onto a fresh WorldObject and adds it to the world (it joins on the next tick). Pass a position, or omit it to spawn at the origin.

world.createFromPrefab(PlayerPrefab);
world.createFromPrefab(ZombiePrefab, { x: 200, y: -120 });

Need an object without a prefab? createEmpty gives you a bare object (with optional tags) you can attach component factories to directly.

import { Circle, CircleGraphics, RigidBody } from '@arcade2d/engine';

const ball = world.createEmpty({ x: 0, y: -200 }, ['ball']);

ball.addComponentsFromFactories({
  graphics: (host) => new CircleGraphics(host, new Circle(16), 0xff9f1c),
  body: (host) =>
    new RigidBody(host, {
      type: 'dynamic',
      collider: { shape: new Circle(16), restitution: 0.6 },
    }),
});

6. Graphics, audio & physics

The engine ships components for the three things every game needs. Attach them like any other component — in a prefab factory or via addComponent.

Graphics

Render an object as a shape or a texture. Graphics components auto-sync to their host's transform every frame, so you never position them by hand. Reach for PolygonGraphics, CircleGraphics, Sprite, AnimatedSprite, TilingSprite, or Text.

import { ImageAsset, Sprite, Texture } from '@arcade2d/engine';

// In a prefab component factory — resolve a preloaded, typed texture:
graphics: ({ assets, object }) => {
  const asset = assets.use(characters).getAs('player', ImageAsset);
  return new Sprite(object, new Texture(asset));
},

Audio

AudioSource plays a clip from an object; the game-level game.audio engine is the master bus with per-category volume.

import { AudioAsset, AudioSource } from '@arcade2d/engine';

const gunshot = assets.use(sfx).getAs('gunshot', AudioAsset);

object.addComponentFromFactory(
  'sfx',
  (host) => new AudioSource(host, gunshot, { volume: 0.6 }),
);

object.getComponent<AudioSource>('sfx').play();

Physics

Physics is opt-in and backed by Rapier. Call initPhysics once (it bootstraps the WebAssembly module) before building a world that uses it, register a PhysicsWorld (step 2), then give objects a RigidBody (step 5).

import { initPhysics } from '@arcade2d/engine';

await initPhysics(); // one-time WASM bootstrap, before createWorld
ESC