Build your first web game.

This guide walks you through installing the CLI, scaffolding a project, writing game logic with the ECS architecture, and shipping to production — all in one place.

01

Prerequisites

NanoForge requires Node.js 25+ and a package manager. We recommend pnpm, but npm, yarn, and bun all work.

Node.js

≥ 25

LTS recommended

Package manager

pnpm / npm / yarn / bun

your choice

TypeScript

optional

highly recommended

Install the NanoForge CLI globally:

terminal
$ npm install -g @nanoforge-dev/cli

# or with pnpm
$ pnpm add -g @nanoforge-dev/cli

# verify the installation
$ nf --version
02

Create a project with editor support

Pass --editor to nf new to scaffold a project that includes the editor entry points and save file at .nanoforge/.

terminal
# create a project with editor support
$ nf new my-game --editor

? What is the name of your project?  my-game
? Which package manager do you want to use?  pnpm
? Which language do you want to use?  ts

 Running schematics
 Installing dependencies...
🚀  Project successfully created!

The --editor flag adds editor-specific dependencies and generates the .nanoforge/editor/ entry points alongside your regular client/ source.

03

Start the editor

Run nf editor from inside your project directory. The editor is a web app that opens at http://localhost:4000.

terminal
$ cd my-game
$ nf editor .

NanoForge Editor
 Editor running at  http://localhost:4000

Load your project

The editor opens on the project loader page. You have two options:

Create new project

Opens a form asking for a project name and local path. The editor initialises a fresh save file.

Import project

Load a previously exported .zip snapshot. Useful for sharing a scene between team members.

Recent projects are cached in the browser so you can reopen them with a single click.

04

Editor interface

The editor UI has three top-level areas: the menu bar, the tab bar, and the main workspace. The menu bar gives you file operations; the tab bar lets you switch between open files.

F

File menu

  • Save — writes .nanoforge/client.save.json
  • Import — load a .zip snapshot
  • Export — export the project as a .zip
  • Exit — return to the project loader
E

Edit menu

  • Undo / Redo
  • Project settings
T

Tab bar

  • One tab per open file
  • Click a tab to switch context
  • Close tabs without losing unsaved work

The editor stores its state in .nanoforge/client.save.json. This file is source-controlled — commit it alongside your code so that scene layouts are tracked in git.

05

Create and edit entities

Inside the workspace, use the scene hierarchy to spawn entities and the component inspector to attach data. Every change is held in memory until you explicitly save.

  1. 1

    In the hierarchy panel, click + New entity. A blank entity appears with an auto-generated ID.

  2. 2

    Select the entity. In the inspector on the right, click Add component and pick Position. Set x: 400, y: 300.

  3. 3

    Add a second component: Velocity. Set vx: 120, vy: 0.

  4. 4

    Go to File › Save (or Ctrl+S). The editor writes .nanoforge/client.save.json.

.nanoforge/client.save.json
// .nanoforge/client.save.json — written by the editor on File › Save
{
  "entities": [
    {
      "id": "entity_42",
      "components": [
        { "type": "Position", "x": 400, "y": 300 },
        { "type": "Velocity", "vx": 120, "vy": 0 }
      ]
    }
  ]
}
06

Generate & run

The editor never touches your source files directly. Instead, nf generate --editor reads the save file and regenerates the editor entry point at .nanoforge/editor/client/main.ts. Run it in watch mode so it picks up every save automatically:

terminal — tab 2
# in a second terminal — regenerate main.ts whenever the save file changes
$ nf generate --editor --watch

NanoForge Generate
 Watching .nanoforge/client.save.json

Then start the game in a third terminal. nf dev builds and serves the client — because the entry point was already regenerated, the game reflects your latest editor changes:

terminal — tab 3
# in a third terminal — run the game with the editor entry point
$ nf dev

NanoForge Dev Mode
 Game running at  http://localhost:3000

Run editor

nf editor . — the editor UI

Generate main file

nf generate --editor --watch — regenerates on each save

Run project in dev mode

nf dev — serves the game

07

Export & share

Use File › Export to download the entire project as a .zip archive. Another developer can import it via File › Import and immediately continue where you left off.

01

Prerequisites

NanoForge requires Node.js 25+ and a package manager. We recommend pnpm, but npm, yarn, and bun all work.

Node.js

≥ 25

LTS recommended

Package manager

pnpm / npm / yarn / bun

your choice

TypeScript

optional

highly recommended

Install the NanoForge CLI globally:

terminal
$ npm install -g @nanoforge-dev/cli

# or with pnpm
$ pnpm add -g @nanoforge-dev/cli

# verify the installation
$ nf --version
02

Create a project

Run nf new to scaffold a new project. The interactive wizard will ask you a few questions about your setup.

terminal
$ nf new

? What is the name of your project?  my-game
? Which package manager do you want to use?  pnpm
? Which language do you want to use?  ts
? Do you want to use strict type checking?  Yes
? Do you want to generate a server for multiplayer?  Yes
? Do you want to skip dependency installation?  No
? Do you want to add a Dockerfile for containerization?  No

 Running schematics
 Installing dependencies...
🚀  Project successfully created!

You can also pass flags to skip the wizard:

terminal
$ nf new --name my-game --language ts --server --no-docker
03

Project structure

nf new generates the following layout. All your game logic lives in client/ (browser) and optionally server/ (Node.js, for multiplayer).

project layout
my-game/
├── client/                  # browser-side entry point
│   └── main.ts
├── server/                  # node.js server (multiplayer)
│   └── main.ts
├── nanoforge.config.json    # build & runtime config
├── package.json
└── tsconfig.json

The generated client/main.ts exports a main() function that NanoForge calls to boot your game:

client/main.ts
import { type IRunOptions } from '@nanoforge-dev/common';
import { NanoforgeFactory } from '@nanoforge-dev/core';

export async function main(options: IRunOptions) {
  const app = NanoforgeFactory.createClient();

  // register libraries, then init and run
  await app.init(options);
  await app.run();
}
04

Core concepts

NanoForge uses an Entity-Component-System (ECS) architecture. Everything in your game — players, projectiles, tiles — is an entity made of components, processed by systems.

E

Entity

A unique ID in the world. Has no data of its own — it is the sum of its components.

player, bullet, tile

C

Component

Plain data attached to an entity. A Position, a Velocity, a Health value.

Position, Velocity, Health

S

System

A function that runs every tick, queries entities with specific components, and transforms them.

moveSystem, drawSystem

Systems never know about each other. They only query the components they care about. This makes it easy to add, remove, or replace behaviour without touching unrelated code.

05

First game logic

Let's build a moving square. First, define two components to describe an entity's position and velocity:

client/components.ts
export class Position {
  constructor(
    public x: number,
    public y: number,
  ) {}
}

export class Velocity {
  constructor(
    public vx: number = 0,
    public vy: number = 0,
  ) {}
}

Then write a system that reads those components and moves entities each tick. The getZipper call returns only entities that have all the listed components:

client/systems/move.system.ts
import type { Registry, Context } from '@nanoforge-dev/ecs-client';
import { Position, Velocity } from '../components';

export function moveSystem(registry: Registry, ctx: Context) {
  const entities = registry.getZipper([Position, Velocity]);

  for (const entity of entities) {
    entity.Position.x += entity.Velocity.vx * ctx.deltaTime;
    entity.Position.y += entity.Velocity.vy * ctx.deltaTime;
  }
}

Register the ECS library, then spawn an entity and attach components in your main() function. The registry is the entry point for all entity and system management:

client/main.ts
import { EcsClientLibrary } from '@nanoforge-dev/ecs-client';
// ... other imports

export async function main(options: IRunOptions) {
  const app = NanoforgeFactory.createClient();
  const ecs = new EcsClientLibrary();
  app.useComponentSystem(ecs);

  await app.init(options);

  const registry = ecs.registry;

  const player = registry.spawnEntity();
  registry.addComponent(player, new Position(400, 300));
  registry.addComponent(player, new Velocity(120, 0));
  registry.addSystem(moveSystem);

  await app.run();
}

To render the entity, add a draw system using the @nanoforge-dev/graphics-2d library:

client/systems/draw.system.ts
import { Graphics2DLibrary, Rect } from '@nanoforge-dev/graphics-2d';
import type { Registry, Context } from '@nanoforge-dev/ecs-client';
import { Position } from '../components';

export function drawSystem(registry: Registry, ctx: Context) {
  const graphics = ctx.libs.getGraphics<Graphics2DLibrary>();
  const entities = registry.getZipper([Position]);

  for (const entity of entities) {
    const rect = new Rect({
      x: entity.Position.x,
      y: entity.Position.y,
      width: 32,
      height: 32,
      fill: '#7c3aed',
    });
    graphics.stage.add(rect);
  }
}
06

Run in dev mode

nf dev compiles your project, starts both client and server, and watches for changes with hot reload.

terminal
$ cd my-game
$ nf dev

NanoForge Dev Mode
 Game running at  http://localhost:3000

Hot reload

Save a file and the engine recompiles and reloads without losing entity state.

Source maps

TypeScript errors point to your source, not the compiled output.

Config watch

Changes to nanoforge.config.json are picked up automatically.

Dev tools

Inspect entities and tweak components live with the built-in visual editor.

07

Install packages

The NanoForge Registry hosts ready-to-use components, systems, and templates. Install them with nf install:

terminal
# install a community component on the client side
$ nf install base/physics-2d

# install on the server side
$ nf install --server base/physics-2d

# install as a plain library (no component/system scaffold)
$ nf install --lib @nanoforge-dev/input

Once installed, packages are imported like any TypeScript module. The CLI also generates stubs for components and systems so you can start using them immediately.

base/physics-2d
base/tilemap-loader
base/input-manager
base/ui-components
Browse all
08

Build & deploy

When you're ready to ship, build your project and start the production server:

terminal
# compile client and server
$ nf build

NanoForge Build
  Building Client...
🚀  Build succeeded!

# start the production server (serves on port 3000 by default)
$ nf start

# custom port and HTTPS
$ nf start --port 8080 --cert cert.pem --key key.pem

You can also manage deployments directly from the Project Manager — a web dashboard where you can monitor builds, manage multiple projects, and invite collaborators.

NanoForge games are served as a URL — no client install, no plugins. Your players open a link and the game runs.

Next steps