Skip to main content

Quickstart

You build an EigenInteractive game in your own repository. The engine repositories are ordinary dependencies; game implementors do not clone them.

Prerequisites

Node.js 22 or newer, npm or pnpm, and Flutter 3.44 or newer — which brings the Dart 3.12 the client needs. Scaffolding also needs network access: it installs both halves as it goes.

A Cloudflare account and Firebase project are needed to run the complete app and deploy, but not to scaffold the project or test game rules. The Android toolchain is needed only to run on Android.

Prerequisites covers each of these with install links, and gives you one command block that checks the lot.

Scaffold both halves

# pnpm
pnpm create eigen-game my-game

# or npm
npm create eigen-game@latest my-game

my-game is the only naming argument. It is a lowercase kebab-case slug; the scaffolder derives My Game, my_game, and the MyGame type prefix from it.

It then asks one question — your organization in reverse domain notation, defaulting to com.example:

Organization in reverse domain notation [com.example]: dev.yourname.games

That becomes the Android applicationId, which is worth getting right at scaffold time: Google Play treats it as the permanent identity of the app and it cannot be changed after the first upload. Pass --org dev.yourname.games to answer it up front, which is also how it works with no terminal attached.

The scaffold is committed when it finishes, so your first git diff is your first game change rather than the ninety generated files underneath it — launcher icons, splash screens and web icons are all written at scaffold time. --no-git skips it, as does scaffolding inside a repository you already have.

The engine and eigen_flutter versions it writes are fixed in the scaffolder, built and tested as a pair before it ships. So the version of create-eigen-game you run decides both — see Versions and compatibility. Use @latest rather than a cached older copy.

The default is a single repository:

my-game/
├── server/ # Cloudflare Worker and authoritative TypeScript rules
└── app/ # Flutter app and presentation rules

The scaffold intentionally supports only this combined layout. It composes a canonical C3-style Cloudflare Worker template with flutter create --empty --platforms android,web, installs both halves, emits the initial game-contract.json, and generates the initial Dart payload types and rules base. The generated files use only public npm/pub.dev contracts, so teams that prefer separate repositories can create either half by hand. The scaffold is convenience, not a runtime requirement.

Prefer to create the repositories yourself or add EigenInteractive to an existing app? Follow Set up without the scaffolder. It uses the same public contracts and supports independent Worker and app repositories.

Generate the game contract

The scaffold has already performed the first generation. After changing state, observation, action, config, or a shared fixture, regenerate:

pnpm run contract # from the generated repository root
# npm run contract is supported too

game-contract.json is the game-owned boundary between repositories. It contains every schema version plus the validated twin fixtures. Commit it, publish it as a release artifact, or copy it into the app build; no particular repository layout is assumed.

The root command also generates the Dart payload library and fixture copies. The underlying commands remain independently usable when the halves live in separate repositories:

cd app
dart run eigen_flutter:generate_payloads \
--contract ../server/game-contract.json \
--output lib/game/generated/payloads.dart \
--fixtures-output test/fixtures
flutter test

The output is immutable plain Dart with deep value equality, field-aware decode errors, and a typed abstract rules base. A game does not install Freezed, json_serializable, build_runner, code_builder, or dart_style for these payloads; the executable owns its generation implementation.

The development loop

The scaffold includes one v1 fixture and both fixture runners. Keep the TypeScript runner watching while editing rules:

cd server
pnpm run test:watch # or: npm run test:watch

After changing a schema or fixture, refresh the cross-language artifact and the generated Dart side:

cd .. # repository root
pnpm run contract

cd app
flutter test

Changing only TypeScript hook behavior does not necessarily change the schemas, but update its shared fixture and run the same sequence: fixtures are part of game-contract.json. wrangler dev reloads Worker source; it does not regenerate the contract or Dart files.

Before anything has shipped, freely edit the seeded v1 unit. Once persisted games or released clients depend on v1, make an incompatible change in a new v2 unit and keep both registry entries.

Run locally

cd server
pnpm dev
curl http://localhost:8787/health

wrangler dev simulates the Worker resources locally. Running the full Flutter app against it additionally requires Firebase configuration; pure rules, fixture, and widget tests do not.

Run the browser at the stable origin already allowed by the Worker scaffold:

cd app
flutter run -d chrome --web-hostname localhost --web-port 7357 \
--dart-define-from-file=app-config.json

Run pnpm firebase:configure (or npm run firebase:configure) from the generated repository root. It configures Android and Web with FlutterFire and generates the service worker's matching public Firebase configuration. Then finish the required public values and VAPID key in app-config.json as shown in Deploy the web app. Copy server/.dev.vars.example to server/.dev.vars and fill the Admin credentials from that same Firebase project before running authenticated Worker traffic.

The Worker template uses Wrangler-generated Env types and a stable GAME_DB binding. Wrangler automatically provisions its D1 database on first remote use; the deploy script applies the engine migrations before deploying.

Keep generation honest

Run generation in write mode during development and check mode in CI:

pnpm run contract:check # generated repository root

When the two halves are separate repositories, promote one exact game-contract.json by checksum. Deploy an Android build supporting a new game schema before server responses start requiring it. Web clients can show the same update state and reload to fetch the latest deployed bundle.

Next, read Your first game and Payload types.