Skip to content

Installation

Scaffold a new game project with the CLI:

Terminal window
npx @doodle-engine/cli create my-game

The scaffolder will prompt you:

  1. Project name: directory name for your game
  2. Use default renderer?: whether to include the batteries-included GameRenderer or start with a custom setup

Then install and run:

Terminal window
cd my-game
npm install # or: yarn install / pnpm install
npm run dev # or: yarn dev / pnpm dev

Your game is now running at http://localhost:3000.

If you prefer to set up manually, install the packages:

Terminal window
npm install @doodle-engine/core @doodle-engine/react
npm install -D @doodle-engine/cli vite

You’ll need:

  • Node.js 24+
  • npm, yarn, or pnpm
  • TypeScript 5.7+
{
"scripts": {
"dev": "doodle dev",
"build": "doodle build",
"validate": "doodle validate",
"preview": "vite preview"
},
"dependencies": {
"@doodle-engine/core": "latest",
"@doodle-engine/react": "latest",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@doodle-engine/cli": "latest",
"vite": "^6.0.0"
}
}

Create src/main.tsx:

import { createRoot } from 'react-dom/client';
import App from './App';
import './index.css';
createRoot(document.getElementById('root')!).render(<App />);

Create src/App.tsx using GameShell:

import { useEffect, useState } from 'react';
import type {
ContentRegistry,
GameConfig,
AssetManifest,
} from '@doodle-engine/core';
import { GameShell } from '@doodle-engine/react';
export default function App() {
const [content, setContent] = useState<{
registry: ContentRegistry;
config: GameConfig;
} | null>(null);
const [manifest, setManifest] = useState<AssetManifest | null>(null);
useEffect(() => {
Promise.all([
fetch('/api/content').then((res) => res.json()),
fetch('/api/manifest').then((res) => res.json()),
]).then(([contentData, manifestData]) => {
setContent({
registry: contentData.registry,
config: contentData.config,
});
setManifest(manifestData);
});
}, []);
if (!content || !manifest)
return (
<div className="app-bootstrap">
<div className="spinner" />
</div>
);
return (
<GameShell
registry={content.registry}
config={content.config}
manifest={manifest}
title="My Game"
/>
);
}

GameShell now requires a manifest prop. The manifest is served by npm run dev at /api/manifest and generated by npm run build at dist/api/manifest. It tells the engine what assets to load and when.

In production, npm run build generates a service worker (dist/sw.js) that precaches all game assets. Assets load progressively before each screen renders, so portraits, music, and backgrounds are ready before the player sees them.

Create a content/ directory with at minimum:

  • content/game.yaml: game configuration
  • content/locations/: location YAML files
  • content/locales/en.yaml: English strings

See Project Structure for the full layout.