Getting Started
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.
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:
$ npm install -g @nanoforge-dev/cli # or with pnpm $ pnpm add -g @nanoforge-dev/cli # verify the installation $ nf --version
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/.
# 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.
Start the editor
Run nf editor from inside your project directory. The editor is a
web app that opens at http://localhost:4000.
$ 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.
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.
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
Edit menu
- – Undo / Redo
- – Project settings
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.
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
In the hierarchy panel, click + New entity. A blank entity appears with an auto-generated ID.
- 2
Select the entity. In the inspector on the right, click Add component and pick
Position. Setx: 400,y: 300. - 3
Add a second component:
Velocity. Setvx: 120,vy: 0. - 4
Go to File › Save (or Ctrl+S). The editor writes
.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 } ] } ] }
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:
# 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:
# 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
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.
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:
$ npm install -g @nanoforge-dev/cli # or with pnpm $ pnpm add -g @nanoforge-dev/cli # verify the installation $ nf --version
Create a project
Run nf new to scaffold a new project. The interactive wizard will
ask you a few questions about your setup.
$ 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:
$ nf new --name my-game --language ts --server --no-docker
Project structure
nf new generates the following layout. All your game logic lives
in client/ (browser) and optionally
server/ (Node.js, for multiplayer).
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:
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(); }
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.
Entity
A unique ID in the world. Has no data of its own — it is the sum of its components.
player, bullet, tile
Component
Plain data attached to an entity. A Position, a Velocity, a Health value.
Position, Velocity, Health
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.
First game logic
Let's build a moving square. First, define two components to describe an entity's position and velocity:
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:
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:
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:
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); } }
Run in dev mode
nf dev compiles your project, starts both client and server, and
watches for changes with hot reload.
$ 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.
Install packages
The NanoForge Registry hosts ready-to-use components, systems, and templates. Install them with nf install:
# 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.
Build & deploy
When you're ready to ship, build your project and start the production server:
# 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.