Motoko makes games now
For six years Motoko has done one thing: run canisters on the Internet Computer. That was
the whole job, and it was a good job.
Starting today it does another one. moxzi.game is a 3D game engine where the entire game
is written in Motoko. Not scripting. Not a binding layer over a C++ engine. The Motoko
you already know, compiled and driving a real renderer.
How real? The demo game runs 2,048 independent Motoko actors — and it does it on an
iPhone 16 Pro at 30 frames a second. Two thousand actors thinking for themselves, in
your pocket, smooth.
Be clear about what this is: a technology demonstration, not a finished game engine.
There’s no editor, no asset store, no physics, no animation system. What there is: a real
language, a real renderer, tools you can install in one line, and a real game you can play
right now to prove the idea works.
Here it is — running in your browser, no install(you probably need latest chrome/firefox):
moxzi.ai/demos/lanternfall/
You already know how to do this
Here’s the thing nobody tells you about game programming: most of it is bookkeeping. A game
is full of things — a monster, a door, a delivery truck — and each thing has state and
does stuff. Traditional engines make you register that thing in a table, describe its shape
to a serializer, hand it an ID, and look it up through a manager whenever anything wants to
talk to it.
But “has its own state and does stuff and you send it messages” is just an actor. You’ve
been writing these for years.
So in moxzi.game, a game entity is an actor. Here’s a courier from Lanternfall, the
game in the demo — a little lamp-carrier that wanders an island collecting berries:
actor* class Courier() {
var energy = 1_000;
var job : Job = #gather;
var cargo : ?Cargo = null;
public func order(value : Job) : async* Bool {
job := value;
true
};
}
That’s a complete game entity. It has energy, it has a current job, it might be carrying
something. Nothing else is required — no schema, no registration, no save() and load()
to keep in sync. energy is a number in a var, exactly where you put it.
Make one the way you’d make anything
let courier = Courier();
Lanternfall does that 2,048 times. Two thousand couriers, each with its own energy, its own
job, its own memory of where the berries are — all inside one program, all independently
addressable, all running on a phone.
That’s actor*, a new Motoko feature that just shipped in moxzi Alpha 6. Before it,
“many independent actors” meant many canisters. Now a single program can hold thousands.
It was built for other reasons entirely, and then it turned out to be exactly what a game
needs.
They talk to each other the way actors talk
When a courier reaches the depot and hands over its berries, it doesn’t fire an event or
post to a queue. It calls the depot:
switch (await* depot.deposit(cargo)) {
case (#accepted) { cargo := null };
case (#rejected(reason)) { ... };
}
If you’ve written an inter-canister call, you’ve written this. The depot’s state is the
depot’s business — the courier can’t reach in and change it, same as always. Two thousand
entities negotiating with each other, and every negotiation is a typed call you can read.
How does any of it get on screen?
You don’t write graphics code. You write one function that answers “given the state of the
world, what does it look like?” — and the engine draws whatever you return.
Lanternfall decides the colour of a patch of ground like this:
if (not lit) material_shadow
else if (i % 7 < 2) material_moss_violet
else material_moss
Unlit ground is dark. Some of it is violet moss. The rest is green. Six lines and you have
a moonlit woodland.
Art is a folder of small YAML files
Those names — material_moss_violet — are not strings you got right by luck. They come
from your assets folder.
Want a new violet moss? Make a file called moss_violet.material.yaml:
version: 1
base_color_linear: [0.07, 0.055, 0.14, 1]
metallic: 0
roughness: 0.8
emissive_linear: [0, 0, 0]
double_sided: true
That’s a real file from Lanternfall, in full. A colour, how shiny it is, whether you can
see it from behind.
Next build, moxzi reads the folder and writes you a Motoko module:
public let material_moss_violet : Nat32 = 31;
Now material_moss_violet is a normal Motoko name. Autocomplete finds it. Typo it and the
compiler stops you — you don’t ship a game with a missing texture and discover it in a
playtest. Lanternfall’s folder generates 93 of these.
3D models work the same way: drop courier.glb in the folder (the standard format every
modelling tool exports) and you get asset_courier. moxzi leaves a small .meta.yaml
beside each file holding a stable ID, so renaming art doesn’t break saved games.
The whole art pipeline is: put a file in a folder. Sound, models, materials, and the UI
all the same.
Buttons are YAML too
You list them in a small config file:
buttons:
- id: gather
label: Gather
action: task_gather
and then handle them in Motoko:
if (action == "task_gather") { await* courier.order(#gather) }
That is the whole path from a player tapping a button on their phone to a courier changing
its mind.
Lanternfall
Go play it: moxzi.ai/demos/lanternfall/
A storm is coming and the island has 128 beacons that need to stay lit. You have 2,048
couriers, 384 groves of glowing fruit, and no way to micromanage any of it. Tap a courier
and tell it to gather, or light a beacon, or go scout. The dark parts of the map become
visible as lanterns come on.
The browser build needs no install and no server — desktop Chrome or Firefox, because
mobile Safari hasn’t shipped the graphics API it uses yet. That’s a browser limitation,
not a device one: the same game, built as a native app, runs on an iPhone 16 Pro at 30 fps
with all 2,048 couriers awake. If you open the demo on a phone the page will tell you
plainly instead of hanging.
Why this matters beyond the game
Motoko was built for one host. moxzi — the self-hosted compiler this all runs on — took it
off that host: it compiles on your laptop, in a browser tab, and on-chain, and the output is
the same bytes every time. moxzi.game is the first thing built on top of that freedom.
If you learned Motoko to write canisters, that knowledge just became transferable to a
completely different kind of software. Same language, same actors, same await*. New
medium.
Getting the tools
Step one — install the moxzi compiler. It’s what builds your game’s Motoko:
curl -fsSL https://moxzi.ai/install.sh | sh
macOS and Linux. The script picks your platform’s build, checks it against the release’s
SHA-256 before unpacking, and installs into ~/.local — binaries in bin/, the compiler
wasm in lib/moxzi/. It refuses to install if it can’t verify the checksums.
Step two — the game tools. Separate installer, because most people who want a Motoko
compiler do not want a 3D renderer, and the two are licensed differently:
curl -fsSL https://moxzi.ai/install-game.sh | sh
That’s moxzi-game, the player, and a complete starter game, about 35 MB. Both installers
check every download against the release’s SHA-256 before unpacking and abort rather than
install something they can’t verify. Apple Silicon macOS today — the script refuses other
hosts by name instead of letting you find out after the download.
Step three — point the builder at the compiler.
export MOXZI_BIN=~/.local/bin/moxzi
export MOXZI_COMPILER=~/.local/lib/moxzi/compiler.wasm
export MOXZI_LINKER=~/.local/lib/moxzi/linker.wasm
Playing something in about a minute
You don’t start from an empty folder. Pong ships with the tools — a real game in four
Motoko files. Copy it out and build it:
cp -R ~/.local/lib/moxzi-game/examples/pong ~/pong && cd ~/pong
moxzi-game build . out/pong
moxzi-player out/pong native
That’s a window with a playable game in it. Now open the folder and see how little there
is:
game.yaml entry point, asset folders, simulation rate
src/Main.mo the match actor — state, input, what the renderer gets
src/Game.mo the rules: collisions, serves, first to seven
src/Opponent.mo the AI paddle, an independent actor
src/Scene.mo a pure function from game state to what's on screen
assets/ materials, models and sounds, as small YAML files
Three edits worth making before you read any documentation: change the ball speed in
Game.mo, change the court colour in assets/court.material.yaml, add a button to
assets/controls.ui.yaml and handle its action string in Main.mo. Rebuild after each.
That’s the whole engine, really.
(Pong is plain persistent actor — deliberately the simple case. actor* and two
thousand of them is what Lanternfall is for.)
Docs and the full walkthrough: moxzi.ai/game/
Don’t learn this by hand — let your AI do it
This is the part I’d actually recommend, and it’s why the docs are shaped the way they are.
Every piece of moxzi ships with a skill file — a single markdown document written for a
coding assistant, not a human: the syntax, the flags, the project layout, the mistakes to
avoid. They’re at moxzi.ai/skills/, and the one you want is
moxzi-game.md.
Hand that file to Claude Code, Cursor, or whatever you use, point it at your copy of the
Pong example, and ask for something small:
“Read this skill file. In ~/pong, add a second ball that spawns after the first goal
and moves at half speed.”
You’ll have something moving in minutes, and you’ll have learned the shape of the thing by
reading a working diff instead of a reference manual. Nobody should be memorising a
preview API by hand in 2026.
What I’d love to hear
- You’ve never made a game. Install it, build Pong, and try to add one thing.
Where did you — or your AI — get stuck? - Does “an entity is an actor” match how you think about a game world, or does it break
down as soon as you try something real? - What would you build with two thousand independent, persistent, upgradeable things
running in someone’s pocket?