- Python 45.8%
- HTML 45.3%
- TypeScript 5.6%
- CSS 1.4%
- Dockerfile 0.9%
- Other 1%
Rebase pre-verified: flashinfer pinned at the identical 0.6.16.post3, gate line byte-identical, sm75 kernels still built, fp8-KV validation unchanged, and #53912 requires MTP which stays disabled. Measured on the 21k agent-turn bench vs the v0.27.1 baselines: cold 21k prefill 12.6s -> 9.8s, decode @20k 46-49 -> 51.4 t/s, short 54 -> 56 t/s, KV pool identical at 971,740 tokens, no CUDA errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| audio-server | ||
| brain | ||
| cli | ||
| docs | ||
| experiments | ||
| graphify-out | ||
| opencode | ||
| playtest | ||
| scripts | ||
| server | ||
| studio | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| docker-compose.build.yml | ||
| docker-compose.yml | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| requirements-dev.txt | ||
| ruff.toml | ||
| trogtoo.md | ||
Trog
A self-hosted collaborative AI game development studio.
Give trog a one-sentence game idea. A checkpointed LangGraph state machine walks it through a real production process — ideation, prototypes, a vertical slice, milestone builds, alpha, beta, ship — writing code and generating art, music, and sfx along the way, while humans watch, steer, and judge from a web studio. The result is a playable web game: a URL you send to friends.
flowchart LR
IDEA(["💡 game idea"]) --> STUDIO
subgraph STUDIO["T r o g"]
direction LR
AGENTS["🤖 agents<br/>design · code · art · music"] <--> PEOPLE["🧑🤝🧑 people<br/>watch · steer · judge · play"]
end
STUDIO --> GAME(["🕹️ a playable web game<br/>a URL you send to friends"])
GAME -. "playtests & feedback" .-> STUDIO
classDef idea fill:#fff3c4,stroke:#d4a72c,color:#1f2328
classDef game fill:#d3f9d8,stroke:#2f9e44,color:#1f2328
classDef ppl fill:#e7f5ff,stroke:#339af0,color:#1f2328
classDef bot fill:#f3f0ff,stroke:#845ef7,color:#1f2328
class IDEA idea
class GAME game
class PEOPLE ppl
class AGENTS bot
Status: pre-1.0, under heavy development.
Table of contents
- Quick start
- Usage
- How it works
- Models: recommended defaults, bring your own
- The production process
- Testing
- Roadmap
- Background
- Contributing
- Acknowledgements
- License
Usage
Install the CLI once (uv tool install --from cli/ trog-cli), point it at
your stack (TROG_URL), then most things are one-liners:
trog run "a cozy fishing game with a day/night cycle" # brief → playable URL
trog image "red car driving at night pixel art" --size 64x64
trog sfx "finger snap"
trog music "calm tavern theme" --seconds 45 --loop
trog animate "knight walking" --base assets/sprite/knight.png
trog critique "a shiny red apple" apple.png # judge any file, real critics
trog playtest trog-games/my-game # run a game headless: gates + shots
trog process-asset-queue # drain whatever the brain queued
trog status # orchestrator · brain · queue
trog test # quick suite, no GPU swaps
trog update --dependencies # guided pin bumps: confirm, deploy, test, keep/revert
How it works
The production process is a checkpointed LangGraph state machine, served by Aegra (Apache-2.0, FastAPI + Postgres): Checkpoints are the quiet superpower: every step can be rewound, edited, forked, and resumed — "undo three decisions ago" is a feature, not a restore-from-backup.
flowchart TD
subgraph WEB["the studio"]
UI["▶️ play · 👀 observe · ✋ interrupt · 📝 edit · 🎨 tweak · 🩺 debug"]
end
subgraph TROGDOR["Trog Server"]
AEGRA["Aegra<br/>Agent Protocol server<br/>auth · SSE streams · runs"]
GRAPH["LangGraph<br/>the production state machine<br/>phases · gates · retries"]
PG[("Postgres<br/>every step checkpointed:<br/>rewind · fork · resume")]
GATE["mcp server<br/>check_phase gates<br/>commit-keyed ledger"]
ZOO["the zoo<br/>image · music · sfx · video<br/>models hot-swapped per job"]
PLAY["playtest<br/>headless browser<br/>screenshots · console · video"]
end
REPO[("📦 game repo on Forgejo<br/>git is truth")]
UI <-->|"stream + resume"| AEGRA
AEGRA <--> GRAPH
GRAPH --- PG
GRAPH --> GATE
GRAPH --> ZOO
GRAPH --> PLAY
GATE --> REPO
PLAY --> REPO
classDef web fill:#e7f5ff,stroke:#339af0,color:#1f2328
classDef core fill:#f3f0ff,stroke:#845ef7,color:#1f2328
classDef data fill:#fff3c4,stroke:#d4a72c,color:#1f2328
class UI web
class AEGRA,GRAPH,GATE,ZOO,PLAY core
class PG,REPO data
| piece | what it is | its one job | status |
|---|---|---|---|
| studio | web app — studio/, React SPA on :2027 |
the window into everything: play, watch, steer | ✅ feed, map, brain selector; files/play panes 🚧 |
| trog CLI + orchestrator | cli/ typer client → orchestrator :8200 |
one-off assets on demand: seats, GPU tenancy, brain bracketing | ✅ |
| aegra | Agent Protocol server (FastAPI + Postgres) | run & stream the graphs; checkpoint every step | ✅ |
| the graph | LangGraph state machine (server/graphs/) |
the production process itself — phases, gates, retries | ✅ docs phases; code-writing phases 🚧 |
| the brain | brain/ — llama-swap + pinned llama.cpp |
one endpoint :8090; the request's model field swaps the resident LLM |
✅ |
| mcp server | thin wrapper over trog_lib verification |
deterministic check_phase, commit-keyed ledger |
✅ |
| the zoo | ComfyUI + audio seats, raised per job | image / music / sfx / video, models hot-swapped | ✅ |
| playtest | headless browser service | run the game; screenshots, console, gates, video | ✅ |
| game repos | one Forgejo repo per game | git is truth | ✅ |
Principles the code actually enforces:
- The graph owns the process from the first commit. The orchestrator is the design, not an add-on.
- Verification lives in tools, not prompts. A claim isn't done until
check_phasesays so — keyed to the exact commit it ran on. - Every generator has a critic. Sometimes the critic is you.
- Humans are first-class nodes. Interruption, judgment, and play are part of the graph, not exceptions to it.
- Git is truth.
- Code 100% Apache-2.0, 100% self-hosted — nothing phones home. Model weights are guests: recommended defaults are open-weight and swappable, each under its own license (see below).
Models: recommended defaults, bring your own
Trog is model-agnostic by design. The framework talks to seats over plain HTTP contracts; which model answers is configuration:
- Brain (LLM): any OpenAI-compatible endpoint. Self-hosted, the roster
is one YAML file (
brain/rosters/<name>.yaml, picked byBRAIN_ROSTER) — add a GGUF, add an entry, and it appears in the studio's brain selector. PointingBRAIN_URL+BRAIN_API_KEYat a cloud provider works the same way. - Image / animate seats: ComfyUI workflows (
server/trog_lib/workflows/) — swap the checkpoint in the workflow JSON and drop the weights inMODELS_ROOT. - Audio seats: env-pointed checkpoints (
audio-server/).
The defaults below are the winners of trog's own benchmarks (bake-offs and eye/ear tests, receipts in docs/) — recommendations, not requirements:
| role | default | weights license (summary — verify upstream) |
|---|---|---|
| brain (default) | Qwen3.6-27B | Apache-2.0 |
| brain (roster) | Qwen3.6-35B-A3B, Qwen3 Coder Next, Gemma 4 31B | Apache-2.0 / Gemma Terms |
| image | FLUX.2-dev | FLUX Non-Commercial License — weights for non-commercial use; generated outputs may be used commercially |
| image (fine lane) | Z-Image-Turbo + elusarca pixel LoRA | Apache-2.0 (both) |
| animate | Wan 2.2 I2V-A14B + pix3lwalk LoRA | Apache-2.0 (Wan); LoRA per its CivitAI listing |
| music | ACE-Step 1.5 | Apache-2.0 |
| sfx | Stable Audio Open | Stability AI Community License (free under revenue threshold) |
| vocals | HeartMuLa-oss-3B | see upstream |
License summaries are good-faith shorthand, not legal advice — check the upstream license before shipping anything commercial, and swap any seat whose terms don't fit your use.
Hardware profiles: one env var describes your rig
How the models above land on your GPUs is a hardware profile — one
named YAML bundle in server/trog_lib/profiles/ declaring which cards each
generation seat occupies, which workflow serves each image/animate lane,
and which brain roster the rig is sized for. Two stack env vars select it:
TROG_PROFILE=trogdor # seat→GPU maps + workflow lanes (orchestrator)
BRAIN_ROSTER=trogdor # LLM lineup (brain/rosters/<name>.yaml, baked into
# the brain image; keep in step with the profile)
Shipped profiles:
| profile | rig | what changes |
|---|---|---|
trogdor-quality (= trogdor, the default) |
4× 16 GB | the reference layout every benchmark ran on: image seat owns the rig (FLUX.2 sharded), animate on three cards, audio beside it on the fourth. The brain is bracketed — stopped for each seat, restarted after |
trogdor-speed |
4× 16 GB | same box split along its own NVLink/NUMA seam: the brain is resident on GPU0+1 and never stopped, all three seats live on GPU2+3. The brain⇄seat swap disappears; the art gets worse, because klein-4b and Wan TI2V-5B are smaller models than FLUX.2-dev and Wan-A14B |
single-24gb |
1× 24 GB | image runs the single-GPU Z-Image seat, roster shrinks to the 27B at 32K ctx, everything serializes — and i2i/animate jobs fail fast with a clear error naming the profile, because stated trade-offs beat mid-render OOMs |
A profile also says whether the brain yields its cards at all
(brain: {gpus, resident, cpuset}). A resident brain removes the
largest single cost in a run — the cold model swap around every seat — and
is only safe when no seat shares its cards, which the profile loader
enforces rather than trusts.
A profile's GPU sets are also the parallelism truth table: disjoint sets
run beside each other, overlapping sets take turns. Layering is
defaults < profile < env — SEAT_GPUS_IMAGE/_ANIMATE/_AUDIO still
override the card maps for ad-hoc experiments, no file needed.
To fit a new rig: copy a profile, point its lanes at any workflow in
server/trog_lib/workflows/, add a sized roster in brain/rosters/ if the
reference one doesn't fit, set the two env vars. pytest tests/test_profiles.py validates every shipped profile names real
workflows. Full walkthrough: docs/self-hosting.md.
The production process
The graph's shape comes from Richard Lemarchand's A Playful Production Process (the Naughty Dog method, scaled down): timeboxed preproduction, a vertical slice that sets the quality bar, milestone-driven production with playtests and a juice pass, and gates that can send work backward, not just retry.
flowchart LR
I["🌱 ideation<br/>brief → concept"] --> P["🧪 preproduction<br/>tiny disposable prototypes"]
P --> F{"is it fun?"}
F -->|"not yet — iterate"| P
F -->|"yes"| V["💎 vertical slice<br/>small, but final quality"]
V --> Q{"hits the bar?"}
Q -->|"no — back to the lab"| P
Q -->|"yes"| G["🎨 assets<br/>spec → queue → drain"]
G --> M["🏗️ full production<br/>one code-agent session<br/>per milestone"]
M --> R{"does it run?"}
R -->|"no — here are the errors"| M
R -->|"yes, next milestone"| M
R -->|"all built"| A["α<br/>feature-complete"]
A --> B["β<br/>content-complete"] --> S(["🚀 ship"])
classDef gate fill:#ffe3e3,stroke:#e03131,color:#1f2328
classDef ship fill:#d3f9d8,stroke:#2f9e44,color:#1f2328
class F,Q,R gate
class S ship
Two kinds of node do the work. Document phases call the brain and commit one markdown artifact. Build phases hire a developer: the node writes a task, the orchestrator raises an OpenCode seat inside a checkout of the game repo, and the session's work comes back as one commit. The graph never holds a working tree — git is the only truth crossing that boundary, which is what makes a build session something a run can survive.
The does it run? gate is the only one that isn't an opinion: a headless
browser loads the committed build, and a failure goes back to the developer
with the console errors and the missing files attached.
Every ◇ is a LangGraph conditional edge, and the judge at each one can be
an agent or a human. Phases start from template scaffolds
(server/templates/) so the agent never wakes up in an empty directory.
One trog-specific addition: retry caps — a human team can argue with a
gate, but an unattended run must terminate, so each gate has a budget and a
capped gate advances with the override written into the release notes.
An autonomous studio should confess its compromises.
The signature move is the interrupt:
sequenceDiagram
autonumber
participant You
participant Studio
participant Graph as LangGraph
Graph->>Studio: streaming... "implementing double-jump"
You->>Studio: ✋ make the jump floatier
Studio->>Graph: interrupt(guidance)
Note over Graph: checkpoint saved<br/>guidance folded into state
Graph->>Studio: resumed — "lowering gravity, adding coyote time"
You->>Studio: ▶️ play the new build
Asset generation swaps models per job and runs in one of two modes: autonomous (a vision/audio critic judges every asset against its brief) or human-in-the-loop (you judge, from the studio). Verdicts are keyed to the exact commit of the asset they judged — edit the asset and the old verdict is stale by construction.
Testing
One entry point (trog test …), tiered — use the cheapest tier that can
catch the bug you're hunting:
| tier | command | time | proves |
|---|---|---|---|
| quick suite | trog test |
minutes | unit + critics + playtest fixture + mock asset cycle — every tier that needs no GPU swap |
| unit | make test |
seconds | parsers, manifests, lib logic (pure pytest, no services) |
| critic | trog test critics |
seconds | judges the fixture repo's known assets against the standing baseline — zero generation |
| playtest | trog test playtest |
seconds | the good fixture game passes AND the broken sentinel fails |
| mock asset | trog test asset --mock |
minutes | the whole drain path on stub seats with declared verdict expectations — zero GPUs |
| studio UI | trog test ui (--headed to watch) |
~1 min | user-level Playwright run against the live studio, including a real send→stream→reply |
| seat one-off | trog image/sfx/… |
varies | one real seat, one item |
| quality | trog test asset |
~30 min | model-contract truths on the real seats; judge the review page by eye |
| regression | trog test regression |
~40 min | all of the above plus the real asset run, PASS/FAIL report |
Two standing fixtures keep the judges honest, in both directions: the
critic fixture's baseline is 6 pass / 1 fail (the failing orange is a
deliberate sentinel — the file is an apple, the brief says orange), and the
playtest fixture pairs a working game with a broken sentinel that loads
and must still fail.
Roadmap
Work is tracked as milestones on the repo's issue tracker: v1 — single-user autonomous (in flight) → v2 — human in the loop → v3 — multi-user. The end state of v3 is game development as a party activity: friends watching the stream, voting at gates, tweaking assets, and playing every build minutes after it lands.
Background
Trog is the second attempt. The first — Agentic Game Development Studio (deprecated) — taught three expensive lessons, and each one is a founding decision here:
- The orchestrator can't be an afterthought. LangGraph was bolted on late and never fit. In trog, the graph is the process from the first commit.
- Wrong game framework. LLMs write dramatically better web games than Löve/Lua games. The research agrees: OpenGame, GameDevBench, and Phaser 4 shipping AI agent skills in-repo.
- The studio is the project. Long runs where the only status view was logs proved to be a boring way to build games. Visibility, interruption, and human interaction are designed in from the start this time.
Contributing
See CONTRIBUTING.md for the dev setup, the test tiers to run before pushing, and the conventions that aren't obvious (lint-only ruff, no new make targets, fixtures that are supposed to fail). Agent onboarding lives in CLAUDE.md — much of trog is built by the kind of agents it hosts.
Acknowledgements
- Richard Lemarchand's A Playful Production Process — the shape of the graph.
- Aegra, LangGraph, llama.cpp + llama-swap, ComfyUI, and Playwright — the shoulders.
- The model authors in the table above, who publish open weights.
License
Apache-2.0 © Chris Moriarty. SPDX-License-Identifier: Apache-2.0
Model weights are not distributed with this repository and carry their own licenses — see the models table.
