Skip to content

Installation

The quickest way to start a new YAGE game:

Terminal window
npm create yage@latest my-game
cd my-game
npm run dev

The scaffolder asks you to pick a template:

  • Recommended — a playable platformer seed with physics, input, animations, sound effects, enemies, and collectibles. Great for learning the engine.
  • Minimal — just @yagejs/core + @yagejs/renderer and an empty scene. Start from scratch and add packages as you go (src/main.ts has commented-out examples showing how to wire each plugin).

Both templates include a Vite config, TypeScript setup, and an AGENTS.md for AI-assisted development.

If you already have a project or prefer to set things up yourself, install packages individually:

Terminal window
npm install @yagejs/core @yagejs/renderer

Add more as needed:

Terminal window
npm install @yagejs/physics @yagejs/lighting @yagejs/input @yagejs/audio @yagejs/debug

The full list of packages:

PackagePurpose
@yagejs/coreECS, math, events, scheduling
@yagejs/rendererPixiJS v8 rendering pipeline
@yagejs/lightingRadial lights and light-level queries
@yagejs/physicsRapier 2D physics integration
@yagejs/inputKeyboard, mouse, and gamepad
@yagejs/audioSound playback and mixing
@yagejs/particlesParticle emitters and effects
@yagejs/tilemapTile-based level support
@yagejs/uiIn-game UI primitives
@yagejs/ui-reactReact bindings for game UI
@yagejs/debugDev tools and inspectors
@yagejs/saveSave / load state management

@yagejs/core sits at the base of the dependency graph — every other package depends on it, so it is always installed automatically.

The @yagejs/* packages are released as a set. Every release publishes all of them at the same version, and each one requires the others at that same minor. Upgrade them in one step, naming every @yagejs/* package your package.json lists — the three below are an example, not the full set:

Terminal window
npm install @yagejs/core@latest @yagejs/renderer@latest @yagejs/input@latest

Leaving one out is the same as not upgrading it.

Moving one package to a new minor on its own can leave two versions of a shared package installed. A game that upgrades @yagejs/save while @yagejs/renderer stays behind ends up with two copies of @yagejs/core. Each copy has its own service container and its own class identities, so an entity or service made through one is not usable through the other — the same kind of failure as the duplicate renderer above.

npm reports a version conflict for some of these mismatches and resolves others into a second nested copy without reporting anything, so an install that completes is not evidence the versions line up. When npm does report a conflict, upgrade the whole set rather than reaching for --force or --legacy-peer-deps, which install the combination the conflict was about.

Addons under @yagejs-addons/* version independently. Each declares the engine minor it supports, so upgrade the addon in the same step as the engine.

  • Node.js 18 or later
  • npm 9+ (or any compatible package manager — pnpm and yarn work fine)
  • A bundler that supports TypeScript and ESM (Vite is recommended)

If you used create-yage, this is already configured. This section is for manual installs.

YAGE targets Vite 8 or later. If you use @yagejs/physics, its Rapier 2D engine requires WebAssembly, so Vite needs the vite-plugin-wasm plugin to handle .wasm imports:

Terminal window
npm install -D vite vite-plugin-wasm
vite.config.ts
import { defineConfig } from "vite";
import wasm from "vite-plugin-wasm";
export default defineConfig({
plugins: [wasm()],
oxc: {
decorator: {
legacy: true,
},
},
build: {
rollupOptions: {
output: {
keepNames: true,
},
},
},
});

The oxc and keepNames options are only needed if you use @yagejs/save. If your project does not persist game state, you can omit them and use the shorter config:

import { defineConfig } from "vite";
import wasm from "vite-plugin-wasm";
export default defineConfig({
plugins: [wasm()],
});

Vite 8 replaced esbuild with oxc as its TypeScript transformer. oxc only supports TypeScript’s legacy (stage-2) decorator transform — it does not transform stage-3 decorators, and instead passes them through to the browser, which does not yet parse native decorator syntax. If you use @yagejs/save’s @serializable decorator in your project, you will hit a SyntaxError: Invalid character: '@' in the browser without this setting. Enabling legacy: true tells oxc to rewrite @serializable class Foo into the classic Foo = _decorate([serializable], Foo) runtime pattern, which runs in every browser.

@serializable looks up classes by class.name when restoring a snapshot, and Vite 8 ships with the oxc minifier, which mangles class and function names by default. Without keepNames, a minified production build will silently fail to round-trip save data through the SerializableRegistry — scenes will load as empty because the saved type strings no longer match any registered class. Set keepNames: true to preserve the original names across minification.

Create a minimal entry file and run your dev server to confirm everything is working:

import { Engine } from "@yagejs/core";
import { RendererPlugin } from "@yagejs/renderer";
const engine = new Engine();
engine.use(new RendererPlugin({ width: 800, height: 600, backgroundColor: 0x1a1a2e }));
await engine.start();

If a blank game window appears with the configured background colour, YAGE is installed correctly.