Signal Garden: A Game Engine That Keeps Mutating
For Build Small, we built Signal Garden: a tiny sci-fi arcade engine where an LLM keeps inventing new play while the player stays in the loop.
We started with Codex building the engine: a shared grid renderer, input model, scoring, health, objectives, effects, and CC0 audio/assets. The key design goal was that a new mode should be small. Games like snake, tower defense, a reactor run, or a boss fight can be implemented as compact GameMode classes, often under 100 lines.
Once the engine worked, we used Codex to generate a suite of mini-games. Then we tuned them with two feedback sources: bots that test simple strategies, and our own playtesting. That let us adjust difficulty, health, enemy counts, win targets, and pacing without guessing.
The Latency Problem
Then we started the open-ended loop: an LLM continuously mutates games by adding features, changing behavior, and proposing new variants.
The catch is latency. Full semantic code edits are slow. Some base games were evolved by Qwen 3.5 through larger rewrites, but live play cannot wait for a rewrite every round. If a player clears a game before the model finishes, the engine still needs to serve something new.
So Signal Garden uses a hierarchy of changes:
flowchart TD
A[Player clears or requests a new round] --> B[Instant heuristic parameter changes]
B --> C[Validated LLM JSON challenge changes]
C --> D[Background semantic code edits]
D --> E[New mechanics available for later rounds]
- Heuristic parameters: instant changes to health, enemy count, target score, wave pressure, and starting state.
- Structured LLM JSON: validated proposals, recorded as JSONL, for bounded changes like spawning enemies, adding helpers, clearing pressure, or tuning supported goals.
- Semantic code edits: slower background method-level changes, validated as Python diffs before they affect live play.
This means the player always gets a fresh game immediately, while deeper semantic mutations keep generating in the background.
Fine-Tuned For Game Generation
Finally, we fine-tuned the model with SFT for this specific task: continuously generating Signal Garden games and safe mutations. We collected play traces, LLM proposals, validator results, and human feedback, then corrected or rejected outputs based on clarity, fairness, visibility, mode fit, and fun.
The model learned the house style:
- Make readable changes, not noisy chaos.
- Stay inside supported actions.
- Keep live mutations small.
- Avoid changing win conditions mid-round.
- Match the mode's actual mechanics.
Signal Garden is small, but the loop is open-ended: quick parameter changes prevent repetition, structured JSON keeps rounds moving, and background semantic edits keep expanding what the arcade can become.
Run
uv run python app.py
For the full LLM arcade loop, run a llama.cpp OpenAI-compatible server at http://127.0.0.1:8080/v1 before starting the app. The default model alias is signal-garden-qwen35-code-mutation.
Runtime Requirements
The Gradio app requirements are installed by uv sync from pyproject.toml: Python 3.10+, Gradio, and NumPy. The LLM path does not require the OpenAI Python package; it uses stdlib HTTP against an OpenAI-compatible /v1/chat/completions endpoint.
For local LLM play, install llama-server from llama.cpp and serve the model alias at http://127.0.0.1:8080/v1. Check the Python app, uv, local llama-server, and live model endpoint with:
uv run python scripts/check_runtime_requirements.py --require-live-llm






