play ↗Espresso perfecto
hold, pour, release on the line
Write the game once in TypeScript. Ship it to the web, iPhone, macOS, Windows and Discord. Every frame is deterministic, so your agent can play it headless, look at any frame, and find the bug before your players do.
$ npm i -g dotframethen tell your agent: dotframe skills get core
Drag it back. Same frame, same checksum, every time. space ← →
f001how an agent plays
The sim is pure state and a fixed step. An agent mashes inputs through 600 frames in a millisecond and reads the state back as JSON. When it needs to see, it renders that exact frame through the real WebGPU renderer and opens the PNG.
Record a run once and it becomes the test: every change after that has to land on the same checksums. That is how a spill that kept taking lives in a loop showed up, at lives: -12.

snap-600.png
what the agent saw at frame 600
$ dotframe sim --mash 7 --frames 600 --json{ "ok": true, "data": { "frames": 505, "over": true, "checksum": 661060014, "state": { "phase": "done", "served": 3, "drink": "espresso", "lives": 0, "verdict": "MUY LEJOS", … }, "ms": 1 }}f002the whole contract
export function step(game: Game, inputs: number[]): void { game.frame += 1; game.players.forEach((p, i: number): void => { const bits = inputs[i] ?? 0; p.x = Math.max(20, Math.min(WINDOW.width - 20, p.x + (bits & 2 ? 4 : 0) - (bits & 1 ? 4 : 0))); p.y = Math.max(20, Math.min(WINDOW.height - 20, p.y - (bits & 4 ? 4 : 0) + 1)); });}All the state lives in one object and only changes in step(). That is what lets dotframe replay it, snapshot it, and roll it back for netplay. Render only reads.
f003one codebase, five places
scriptc compiles your TypeScript to a real binary. The web build runs the same code on WebGPU.
WebGPU, and Canvas 2D on phones without it
dotframe build weba native app, no web view
dotframe build iosone binary, SDL3 + wgpu-native
dotframe build macoscross-built from your Mac
build-native.sh windowsan Activity in the voice channel
dotframe build discordf004made with dotframe
f005shipped one?
Put it in your README so the next person finds the engine.
<a href="https://dotframe.crafter.games"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/crafter-games/dotframe/main/assets/badge/made-with-dotframe-dark.svg"> <img alt="Made with dotframe" src="https://raw.githubusercontent.com/crafter-games/dotframe/main/assets/badge/made-with-dotframe-light.svg" height="32"> </picture></a>