Getting Started
This is a private project. Reach out to us at www.setsnail.com to get access.
This guide introduces the minimum setup needed to create a world, define components, spawn entities, and run systems.
Prerequisites
Section titled “Prerequisites”Make sure you have Node 24.16.0+ and pnpm 10+, then install the aggregate package for your runtime. This guide uses the Pixi-based 2D engine:
pnpm add @shell/engine-2dCreating a world
Section titled “Creating a world”Everything starts with a World. It holds your entities, components, systems, and resources.
import { Engine2D, World } from '@shell/engine-2d';
const canvas = document.querySelector<HTMLCanvasElement>('#game')!;const world = new World();world.addPlugin(Engine2D({ canvas, resizeTo: window }));@shell/engine-2d re-exports the core ECS, canonical transform APIs, Pixi, physics, and gizmo APIs.
Game-level capabilities such as spatial navigation and tilemaps are installed separately. Use
@shell/engine-minimal for renderer-neutral programs or @shell/engine-3d for Three.js.
Defining components
Section titled “Defining components”Components are pure data. The engine aggregate provides canonical components such as Position.
Define game-specific data with Define.component() and use StoreType to pick the backing storage:
import { Define, StoreType } from '@shell/engine-2d';
export const Velocity = Define.component('velocity').withSchema({ x: { type: StoreType.Float32 }, y: { type: StoreType.Float32 },});Creating a system
Section titled “Creating a system”Systems contain your game logic. Declare which components you need with .queries(), then write the update function.
import { Define, Mut, Position, Schedule } from '@shell/engine-2d';
const Moving = Define.query({ data: [Mut(Position), Velocity] });
const MovementSystem = Define.system() .queries({ moving: Moving }) .update(({ queries, time }) => { queries.moving.forEach((entity) => { entity.$position.x += entity.$velocity.x * time.delta; entity.$position.y += entity.$velocity.y * time.delta; }); });Mut(Position) tells the engine you will write to the position.
Register the system on a schedule:
world.registerSystem(Schedule.Update, MovementSystem);Spawning entities
Section titled “Spawning entities”Entities are lightweight IDs. You can spawn them from prefabs inside systems or from the world directly.
import { Define, Position } from '@shell/engine-2d';
const PlayerPrefab = Define.prefab() .with(Position, { x: 0, y: 0 }) .with(Velocity, { x: 100, y: 50 });
// Inside a systemcommands.spawn(PlayerPrefab);Running the game loop
Section titled “Running the game loop”Mount the world to start the lifecycle. Startup systems run, then the tick loop begins.
await world.mount();If you are writing a test or running a headless simulation, you can tick the scheduler manually:
const scheduler = world.get('scheduler');scheduler.tick();Complete minimal example
Section titled “Complete minimal example”Here is a self-contained example that ties everything together:
import { Define, Engine2D, Mut, Position, Schedule, StoreType, World } from '@shell/engine-2d';
const Velocity = Define.component('velocity').withSchema({ x: { type: StoreType.Float32 }, y: { type: StoreType.Float32 },});
const Moving = Define.query({ data: [Mut(Position), Velocity] });
const MovementSystem = Define.system() .queries({ moving: Moving }) .update(({ queries, time }) => { queries.moving.forEach((entity) => { entity.$position.x += entity.$velocity.x * time.delta; entity.$position.y += entity.$velocity.y * time.delta; }); });
const canvas = document.querySelector<HTMLCanvasElement>('#game')!;const world = new World();world.addPlugin(Engine2D({ canvas, resizeTo: window }));world.registerComponent(Velocity);world.registerSystem(Schedule.Update, MovementSystem);
await world.mount();
// Spawn a playerconst commands = world.get('commands');commands.spawn(Define.prefab().with(Position, { x: 0, y: 0 }).with(Velocity, { x: 10, y: 0 }));Next steps
Section titled “Next steps”- Read the Concepts guide for a deeper look at worlds, queries, resources, plugins, and scenes.
- Compare the engine aggregates and read the canonical transform reference.
- Browse the Core ECS Reference for detailed API documentation.