
The Lighthouse That Wouldn't Turn: Building an AI Game Studio That Plays Its Own Games
I built a studio of AI agents on Amazon Bedrock AgentCore that turns one sentence into a real game, then makes a test player play it before you can. Here's how it works, and how it fixes its own mistakes.
A storm has rolled in over Craghaven, and the whole fishing fleet is still out at sea.
I'm Old Maren, the keeper of the lighthouse. My job is simple. Sweep the beam across the water, light a safe lane past the rocks, and bring the boats home before the storm takes them. A boat drifts in from the right, so I press the arrow key to swing the beam towards it.
The beam doesn't move.
I press it again. I try the other key. I grab the beam with the mouse and drag. It stays exactly where it is, pointing straight up into the night, lighting nothing but sky. Out on the water the boats carry on drifting, the same way they would if I'd got up and gone to make tea.
Nothing I do reaches them.

Harbour Light was a game an AI wrote for me, from one sentence: "a lighthouse keeper guiding fishing boats safely into the harbour through a stormy night". It compiled. It passed every check I had at the time. An agent had read all of its code and signed it off. And nobody could play it, because the code had locked the beam at an angle where there was no sea.
Where the idea came from
It started with a question I kept coming back to. Agents can write whole applications now. So why not a game? Not a mock-up or a demo screen. A real game, with levels and lives and a score, made from nothing but the sentence you type.
The making part felt possible. I could picture a little studio of agents, one designing, one drawing, one writing the code, and you watching them work on screen until a game appeared. That alone would be fun to watch.
But one thing kept bothering me. How would anyone know the game actually worked?
A game isn't like an API. There's no test you can run that says "playable". Code can compile, run without a single error, look exactly like the thing you asked for, and still be impossible to play. The only way to find out is the way players find out. You pick up the controls and try.
So the idea grew a second half. What if the studio played every game before you did? Agents would do the creative work, and something that isn't an agent would play the result, or solve it, and only then let it through. If it failed, it would go back to the agents with the reason, and they'd fix it themselves.
Old Maren's lighthouse was the moment that idea stopped being a nice-to-have. Every check Harbour Light passed had asked a fair question. Does it run? Does the code look like a lighthouse game? None of them had asked the first thing a player asks, which is whether anything they do makes a difference. That question became the centre of PlayProof, and it's why the rule the whole studio follows is short: agents create, code decides.
What PlayProof does
You type one sentence, and a studio of AI agents builds you a game while you watch. The build page shows every agent's work as it happens: the design, the art it picked, the code, every check, every rejection and every fix. When it's done, you press Play.
Watch it happen in the 2:26 demo video .

It makes three kinds of game:
- Arcade games. One-screen action games, a runner or a dodger or a catcher, where the agents write every line of code and the levels never end.
- Proven puzzles. Turn-based puzzles with rules the agents invent and five levels they design.
- Endless puzzles. A puzzle set in the world of your idea, with its own story and characters, that hands you a brand-new level every time you clear one.
Arcade games are tested, by a test player in a real browser. Puzzles are proven, by a solver that has to finish every level. A game that can't pass goes back to the agents, and you never see it.
Inside the studio
Each kind of game has its own team. The agents are built with Strands Agents and run on Amazon Bedrock AgentCore Runtime. Most of them are Claude Sonnet 4.6; two puzzle roles with short, structured jobs use Amazon Nova 2 Lite.

The arcade studio has four agents and two code checks:
- The Game Designer turns your sentence into a design: what you control, what you collect, what you avoid, what's new in each level. It also writes the contract for the test player, the most important thing in the build.
- The Art Director casts the game from PlayProof's art pack, a library of more than 200 characters, creatures, props and backdrops, every one of them drawn in code.
- The Coder writes the whole game on the PlayProof Game Kit, which sits on top of Phaser 3: the levels, the HUD, the sounds, the lifelines, everything.
- The Checker is code. It runs the game in a sandbox with no network, compiles it, plays three levels with random input and builds every level up to 30.
- The test player is code too. It plays the game in a real browser, Amazon Bedrock AgentCore Browser, and decides whether a person could actually play it.
- The Lead Designer reads the code together with the test player's findings and screenshots, and asks for improvements. The Coder applies them, and the polished game is played again before it ships.
The puzzle studio has five agents and a solver. A Director approves the idea and plans the levels. A Game Designer writes the rules as a small game spec: which objects push, slide, fall or get collected, and how you win or lose. An Artist paints every object. A Level Designer draws each level, and the solver searches it for the shortest way to win. A Critic reads the solver's numbers and sends weak levels back to be made better.
The endless studio is the fastest. A Game Designer builds the world, with a title, a story, how to play, a cast and the words on the HUD, and a puzzle engine with its own solver makes every level.
The contract
A test player can't play a game it knows nothing about, and I didn't want it guessing from pixels. So the Game Designer writes a contract alongside the design. It names the controls, out of seven the test player knows (steer left and right, steer anywhere, switch lanes, jump, flap, tap, shoot), and gives every difficulty knob as three numbers: its value at level one, its change per level, and a limit it can never pass.
The Coder has to build the game on that contract. This is the real code it wrote for Nectar Dash, a hummingbird game made from "a hummingbird darting through a flower garden, sipping nectar and dodging grumpy wasps":
1
2
3
4
5
6
7
8
9
10
11
12
13
PP.define({
title: "Nectar Dash",
hearts: 3,
controls: "steer_xy",
tuning: { // [value at level 1, change per level, limit]
target: [500, 120, 2000],
waspCount: [3, 0.3, 10],
waspSpeed: [90, 9, 260],
flowerSpawnGap: [0.9, -0.08, 0.5],
flowerLifetime: [5.0, -0.25, 2.2],
},
// …
});Inside the game, every knob is read through the kit, and everything that matters to a player is tagged:
1
2
3
4
5
const waspCount = Math.round(g.tune("waspCount"));
// …
L.zip = g.player(zipImg); // the hummingbird
g.good(s); // a flower worth flying to
g.bad(s, "dodge"); // a wasp to get away fromThose tags are what the test player sees. It knows where the player is, where the good things are and where the bad things are, and it steers towards one and away from the other with the same keys you'd use.

The limits are what let PlayProof promise levels that never end and never break. Ask for one more wasp per level and by level twenty the screen would be all wasps, so code checks the difficulty curve before any game code exists and eases it if it climbs too fast. In Nectar Dash it did exactly that: one extra wasp per level became 0.3.
The test player
The test player plays every arcade game twice. Once properly, with the game's own controls, and once with its hands off the keyboard. Then it compares the two runs, because a game where playing and not playing end the same way isn't a game.
This is what it said about Harbour Light, word for word:
35 s of scripted input (taps, drags, sweeps, keys) changed neither the score nor the goal progress compared with doing nothing.

The test player plays the game, then leaves it alone. If both runs end the same, the game fails.
That check is now a hard gate, along with four more. A build fails if a level is won the moment it starts, if level one is out of reach for a player using all their lives, if the game sits for fifteen seconds with no score and no progress, or if level ten crashes. A full playtest plays levels one to five in a row and then jumps straight to level ten, so endless really means endless.
It catches what nothing else can. On its first run it found a tower-defence game that crashed the moment you tapped a tower, which the dry run never saw because the dry run never taps anything. Over the next five arcade builds it caught "level won with no input" three times and "doing nothing loses within seconds" three more. Every one of those games compiled, every one had been reviewed by an agent, and every one was fixed before anyone saw it.
It fixes its own mistakes
This is the part I'm proudest of. When a check fails, nobody steps in.
The test player's verdict goes back to the Coder as a sentence, not an error code. It says what failed, how badly, and what a fix has to achieve. The first version of Nectar Dash was rejected like this:
Level 1 is out of reach: a test player using all its lives reached only 18% of the goal in 21.5 s. Level 1 must be clearable in about 30–40 s by a new player: lower its target or make the core verb reach the targets.

The Coder read that, rewrote the game, and in the next version level one was cleared in twenty seconds. Then the Lead Designer looked at the test player's screenshots and spotted wasps parked off the right edge of the screen, something no amount of reading the code would have shown. The Coder fixed that too, and the finished game cleared levels one to four in a row and ran level ten without a problem. Fourteen minutes from a sentence to a tested game, and I didn't touch it once.

The Coder reads why the game was rejected, rewrites it, and tries again, with no human in the loop.
Checker errors work the same way, with the exact line that failed. And if a polish ever breaks something, the version that already passed is the one that ships, so a game never gets worse on its way to you.
Proven puzzles
Puzzles get a solver instead of a player, because a puzzle can be proven. Every level the Level Designer draws is searched for the shortest way to win. No win, no level.
In the build that became First Snow, from "a squirrel pushing pinecones into its tree holes before the first snow", the Level Designer drew a third level nobody could win, and the solver threw it out. The Critic called a three-move first level too short, and the longer one had to be proven again from scratch. When a "better" fifth level couldn't be shown to be better, the studio kept the one it could prove. All five levels shipped proven.
Then every solution is replayed in the same TypeScript engine the browser runs, so the proof and the game can never disagree. On any level, Watch the proof replays the solver's winning moves for you.

Endless puzzles
Endless puzzles are the quickest way to see the whole idea at once. From "a squirrel hopping up a giant oak, grabbing acorns and dodging thorny brambles", the Game Designer wrote Acorn Ascent: a squirrel called Pip with a bottomless appetite, a story, how to play, and a cast from the art pack. The engine drafts levels near a rising difficulty target, proves each one with its solver and replays the proof through its rules.
The build took fourteen seconds and proved the first three levels. After that, every time you clear a level, a brand-new one is made and proven in your own browser while you play. It never runs out, and you never get a level that can't be finished.

How it runs on AWS
The numbers follow one game from the sentence you type to the moment you press Play.
- Amazon CloudFront serves the web app, a React and Phaser 3 build, from a private Amazon S3 bucket that only CloudFront can read.
- When you type your sentence, the app calls an Amazon API Gateway HTTP API.
- An AWS Lambda function (Python 3.12 on Arm) creates the build job.
- Lambda hands the job to the studio on Amazon Bedrock AgentCore Runtime with InvokeAgentRuntime. The runtime starts it as an async task, so a fourteen-minute build runs in the background while the runtime keeps answering health checks.
- The Strands agents call their models on Amazon Bedrock: Claude Sonnet 4.6 for most roles and Amazon Nova 2 Lite for the Director and the Critic. The agents' long instructions, like the Game Kit reference the Coder works from, are cached with Bedrock prompt caching, so the part that repeats every round is read from the cache instead of being paid for again.
- For arcade games, the test player opens a session in Amazon Bedrock AgentCore Browser and plays the game in a real browser, once with the controls and once hands off.
- Every step of the build is written to Amazon DynamoDB as an event, and the finished game (code, levels, proofs and playtest results) is stored in Amazon S3.
- The build page polls Lambda for new events, which is how you watch the studio work live. When the game passes, it's ready to play, and it joins the shelves on its own.

Three design choices hold it together:
- One engine everywhere. The puzzle engines and solvers are a single TypeScript codebase, shared by the browser and the studio's checks. I replayed every proven level of the six puzzle games on the shelf in the real browser engine, and all 24 finished in exactly the number of moves the solver had found.
- Agent code is sandboxed. Every arcade game runs in an iframe with no network and no access to the page around it, and every role in the system can reach only what it needs. The Lambda role, for example, can touch its own table, its own bucket and the runtime, and nothing else.
- The shelves check too. A new game lists itself on the Arcade or Gallery shelf, marked New, only once it has passed: the test player cleared level one and level ten runs, or every puzzle level is proven.
PlayProof in numbers
| Measure | Result |
|---|---|
| One sentence to a tested arcade game | about 14 minutes, every line of code written by agents |
| One sentence to an endless puzzle | about 14 seconds, with new proven levels forever |
| Agents in the studio | 4 for arcade games, 5 for puzzles, 1 for endless worlds |
| Art pack | 200+ pieces, all drawn in code |
| Controls the test player can use | 7 |
How I connected Claude Code to AWS
I built PlayProof with Claude Code running on my Mac. To connect it to AWS, I created a dedicated IAM user for it,
playproof-coding-agent, and gave Claude Code its own AWS CLI profile for that user. Every AWS command it ran used that profile, so every resource in the app, the Lambda function, the API, the CloudFront distribution, the DynamoDB table and the AgentCore runtime, was created by the coding agent, and CloudTrail shows each one under its name.
CloudTrail, filtered to Claude Code's IAM user: the bucket, CloudFront, DynamoDB, IAM roles, Lambda, API Gateway, the budget and the AgentCore runtime, all created by the coding agent.
How I brought PlayProof up on AWS
I built and tested everything locally first, then let Claude Code bring it up on AWS, one layer at a time, all under its own profile:
- The site. A private S3 bucket with CloudFront in front of it, using origin access control so the bucket is never public.
- The data. A DynamoDB table in on-demand mode for jobs, build events and the shelves, and a second private S3 bucket for game files.
- The API. The Lambda function and the HTTP API, with a role scoped to exactly that table, that bucket and the runtime.
- The studio. The agents, the checks and the game engine are packaged as a single zip and deployed to AgentCore Runtime with direct code deployment, so there's no container image or Docker build in the loop. The runtime gets its own least-privilege role: only the Claude and Nova models it uses, AgentCore Browser sessions, its table, its bucket, and its own logs and traces.
- The wiring. Lambda is pointed at the runtime, and a smoke call with a deliberately invalid job proves the runtime answers without spending a single model token.
- The guard rails. An AWS Budgets alarm for the month and a daily one for Bedrock, so a runaway build can never surprise me.
The deploy script is idempotent. Every time the studio changed, the same command uploaded a new package and updated the same runtime in place, and by launch day it had gone out 22 times.
Back in Craghaven

Somewhere in my account, Old Maren is still up in her lighthouse with the beam pointing at the sky. Harbour Light never made it onto a shelf, and it never will. Every game that has, from Nectar Dash to First Snow to Acorn Ascent, was played, solved or proven before anyone pressed Play.
That's what I wanted from the start. Not a game an AI says is finished. A game that has already been played.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article