Skip to content

Your First Game

In this guide you will create a spinning green triangle from scratch. You write the three pieces a YAGE game is built from: a component that holds the behaviour, an entity type that puts the triangle together, and a scene that spawns it. The finished example is under 50 lines of TypeScript.

Loading…
import { Component, Engine, Entity, Scene, Transform, Vec2 } from "@yagejs/core";
import { GraphicsComponent, RendererPlugin, CameraEntity } from "@yagejs/renderer";
import { DebugPlugin } from "@yagejs/debug";
class Spin extends Component {
private readonly transform = this.sibling(Transform);
private readonly speed: number;
constructor(speed = 2) {
super();
this.speed = speed;
}
update(dt: number): void {
this.transform.rotate(this.speed * dt);
}
}
class Triangle extends Entity {
setup(params: { position: Vec2 }): void {
this.add(new Transform({ position: params.position }));
this.add(
new GraphicsComponent().draw((g) => {
g.poly([0, -45, 40, 35, -40, 35]).fill({ color: 0x22c55e });
}),
);
this.add(new Spin());
}
}
class HelloWorldScene extends Scene {
readonly name = "hello-world";
onEnter() {
this.spawn(CameraEntity, { position: new Vec2(400, 300) });
this.spawn(Triangle, { position: new Vec2(400, 300) });
}
}
const engine = new Engine({ debug: true });
engine.use(new RendererPlugin({
width: 800,
height: 600,
backgroundColor: 0x0a0a0a,
}));
engine.use(new DebugPlugin());
await engine.start();
engine.scenes.push(new HelloWorldScene());
import { Component, Engine, Entity, Scene, Transform, Vec2 } from "@yagejs/core";
import { GraphicsComponent, RendererPlugin, CameraEntity } from "@yagejs/renderer";
import { DebugPlugin } from "@yagejs/debug";
  • Component is the base class every custom behaviour extends.
  • Engine is the entry point that orchestrates plugins and the game loop.
  • Entity is the base class for entity types, such as the triangle.
  • Scene is the base class for defining game scenes.
  • Transform stores an entity’s position, rotation, and scale.
  • Vec2 is a 2D vector used throughout the engine.
  • GraphicsComponent draws shapes via the PixiJS v8 graphics API.
  • RendererPlugin sets up the PixiJS v8 rendering pipeline.
  • CameraEntity is the camera — spawn it in your scene. It exposes follow(), shake(), zoomTo(), bounds, and more directly.
  • DebugPlugin enables the FPS counter, entity inspector, and dev overlays.
class Spin extends Component {
private readonly transform = this.sibling(Transform);
private readonly speed: number;
constructor(speed = 2) {
super();
this.speed = speed;
}
update(dt: number): void {
this.transform.rotate(this.speed * dt);
}
}

Spin grabs its sibling Transform component and rotates it every frame. The update(dt) method receives the delta time in seconds, so the triangle turns 2 radians per second at any frame rate.

Game logic in YAGE lives in components like this one. Entities and scenes have no update() method of their own.

class Triangle extends Entity {
setup(params: { position: Vec2 }): void {
this.add(new Transform({ position: params.position }));
this.add(
new GraphicsComponent().draw((g) => {
g.poly([0, -45, 40, 35, -40, 35]).fill({ color: 0x22c55e });
}),
);
this.add(new Spin());
}
}

An entity is a container for components. Triangle extends Entity and builds itself in setup(), which runs when a scene spawns it. The params argument holds what the spawn call passes in, here the position.

setup() attaches three components to the entity:

  1. Transform — places the triangle at the position from params.
  2. GraphicsComponent — draws a green triangle using three vertices defined as a flat [x, y, ...] array.
  3. Spin — the custom behaviour that rotates the triangle every frame.

Every Triangle you spawn gets the same three components.

class HelloWorldScene extends Scene {
readonly name = "hello-world";
onEnter() {
this.spawn(CameraEntity, { position: new Vec2(400, 300) });
this.spawn(Triangle, { position: new Vec2(400, 300) });
}
}

HelloWorldScene extends Scene and implements onEnter(), which runs when the scene becomes active. onEnter() spawns the entities the scene starts with. The triangle’s behaviour stays in Spin, not in the scene.

this.spawn(CameraEntity, { ... }) creates the camera as an entity in the scene. Setting the camera position to (400, 300) shifts the world origin to the top-left corner of the default 800 × 600 viewport. The triangle is then spawned at world position (400, 300) and lands in the middle of the screen. By default the camera is at (0, 0) which puts world origin at the centre of the viewport — see Camera → Coordinate Convention in the Rendering guide for the full explanation.

const engine = new Engine({ debug: true });
engine.use(new RendererPlugin({
width: 800,
height: 600,
backgroundColor: 0x0a0a0a,
}));
engine.use(new DebugPlugin());
await engine.start();
engine.scenes.push(new HelloWorldScene());
  • new Engine({ debug: true }) creates the engine with debug mode enabled.
  • engine.use() registers plugins — here the renderer and debug overlay.
  • engine.start() initialises all plugins and starts the game loop.
  • engine.scenes.push() pushes a scene onto the scene stack.
this.spawn(Triangle, { position: new Vec2(400, 300) });

this.spawn() creates an entity of the given class and passes the second argument to its setup(). TypeScript checks that argument against the setup() parameter, so a missing field is a compile error. The debug tools list the entity under its class name, Triangle. CameraEntity is spawned the same way: it is an entity type that @yagejs/renderer provides.

An entity that exists once and has no behaviour of its own, such as a background, does not need a class. Pass spawn() a name instead, and add the components to the entity it returns:

const background = this.spawn("background");
background.add(new Transform());
background.add(
new GraphicsComponent().draw((g) => {
g.rect(0, 0, 800, 600).fill({ color: 0x111827 });
}),
);

Anything you spawn more than once, or that has behaviour, is an entity class like Triangle.

Start your dev server (e.g. npx vite) and open the page in a browser. You should see a green triangle spinning smoothly on a dark background with the debug overlay in the corner.

  • Add keyboard input with @yagejs/input to control the triangle.
  • Swap the shape for a sprite using SpriteComponent from @yagejs/renderer.
  • Read Entity Subclasses for more on entity types and setup().
  • Read Project Structure to understand the full package layout.

Coding agents: fetch https://yage.dev/llms.txt first and prefer the Markdown references it links over these HTML pages. This page's Markdown counterpart is /llms/quick-start.md.