# incanto

**Vibe-coding-first web game engine SDK.** Every scene, character, event, and multiplayer
rule is JSON an AI agent can read, diff, and rewrite — rendered by three.js.

## Start with a whole game

```bash
bunx incanto new my-game        # Beacon Isle — the flagship 3D template
bunx incanto new my-2d --template platformer-2d   # the 2D flagship
cd my-game && bun install && bun run dev
```

One command scaffolds a complete island action-adventure: a **generated
golden-hour world** (terrain, sea, groves, grass — `bun run world` re-rolls the
whole island deterministically), a quest NPC with dialogue and choices, enemies
that **hunt you across the terrain** via grid navigation, melee with a sword
riding the character's hand bone, bloom + color-grade, touch controls on
phones, win fireworks and a death/respawn loop — in ~2 files of game code, the
rest declared in JSON. `bun run verify` plays the entire quest **headlessly**
and replays recorded input bit-identically: the agent loop is author → verify →
fix, no browser needed.

`bunx incanto new --list` shows all six starters, grouped by dimension: three
3D (island adventure, third-person shooter, quest vignette) and three 2D
(platformer, survivors-like, and a whack-a-mole played with the mouse alone).

Every starter is **TypeScript + Vite + React**: `src/main.tsx` mounts `<App />`
and `src/App.tsx` owns the canvas the engine draws into. React is the shell, not
the game — `incanto/react`'s `<IncantoCanvas>` is the other shape, for putting a
game inside an app you already have.

## Or wire the engine yourself

```bash
bun add incanto three
```

```ts
import { Engine, loadScene } from 'incanto';
import { Renderer2D, enablePhysics2D, registerNodes2D } from 'incanto/2d';

registerNodes2D();
const scene = loadScene(levelJson); // the whole game lives in *.scene.json
const engine = new Engine();
engine.setScene(scene);
engine.input.attachKeyboard(window);
new Renderer2D({ canvas, engine });
await enablePhysics2D(engine);
engine.start();
```

| Entry | Contents |
|---|---|
| `incanto` | node tree, scene JSON load/serialize, signals, Engine loop, InputMap, Behavior, Timer |
| `incanto/2d` | sprites, spritesheet animation, Camera2D, labels/UI, Rapier 2D physics, character controller |
| `incanto/3d` | meshes, lights, Camera3D, Rapier 3D physics |
| `incanto/net` | multiplayer over a pluggable `NetworkTransport` — built-in offline Loopback + [@agent8/gameserver](https://www.npmjs.com/package/@agent8/gameserver) adapter (optional peer); custom backends implement one interface |

- `schemas/scene.schema.json` — generated JSON Schema for scene files (the agent contract)
- `templates/agent8-server.ts` — the multiplayer server kernel for the agent8 platform (`.js` beside it for the legacy single-file server)

## Debug mode

Off by default, with **no URL toggle** — a deployed build can never be switched on by a player.
Gate it to your own dev server (every template already ships the line):

```ts
createGame3D({ ..., debug: import.meta.env.VITE_INCANTO_DEBUG === '1' });
```

```bash
VITE_INCANTO_DEBUG=1 bun run dev   # overlay ON; plain `bun run dev` and prod builds = off
```

A **☰ debug** menu appears top-left: **Explorer** (live scene tree; the selected node's bounds
light up orange in-game), **Inspector** (its props, editable while running), **Logs**
(`engine.log` tail with filters), **Stats** (fps · frame ms · nodes · tris · draw calls), and
**Colliders** (physics shapes as wireframe; one click cycles off → all → **selected**, and
`selected` draws only the collider of whatever the Explorer has picked — the way to find one shape
in a world full of them). In code: `physics.debugDraw = true` + `physics.debugScope = node`, or
`(await import('incanto/debug')).attachDebugOverlay(engine)` to boot the overlay by hand.

## The agent8 asset library

`bunx incanto editor --token <v8 access token>` (or `INCANTO_V8_TOKEN`) adds a
**📚** button to every field that takes a resource — model, texture, sprite
sheet, sound. Shelves, search, real previews (a GLB is rendered by the engine
itself), and one pick writes the URL, or the scene `assets{}` entry plus its
`$ref` for fields that take one. Without a token the panel asks for one and
keeps it in the browser; the editor server proxies the catalog because it is
per-account and not CORS-open to localhost.

## Play ⇄ edit, in one window

The menu's last item, **✎ edit this scene**, turns the running game into the scene editor in the
page you are already in — same window, no reload, and the edit camera starts where the player's
eye was. The editor's **▶ play** offers the way back as a choice: **preview here** (simulate in
the viewport, scripts stripped) or **run as the game** (this page becomes the real game again,
with your edits). **✕ exit** returns without playing.

It follows `debug`, so an overlay build already has it. `editor: false` opts out;
`editor: { save }` gives the editor a save button that writes your file; `game.openEditor()` is
the same switch as a function. The editor loads lazily (`incanto/editor`) the first time it is
asked for — a game that never opens it never downloads it.

Docs, agent skills, examples, and architecture: **https://github.com/planetarium/Incanto**

© 2026 Verse8. All rights reserved. Proprietary — see [LICENSE](./LICENSE). Bundled third-party OSS notices: [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md).
