Inspiration
There are a lot of tower defence games. A lot of bird hunting games, a lot of village builders. We wanted to do ours a little differently.
What we kept coming back to was routes. Almost every tower defence hands the enemy a path and then asks you what to put beside it. We wanted enemies that move freely instead, that come from wherever they like and pick their own targets when they get there.
We were talking about how birds already do exactly that, and the idea hit us from the other side. Why not have the birds attacking us?
Everything else followed from taking that seriously. A village of farmers under an open sky, where the things you build to stay alive are the same things that bring the birds down on you. The field feeds you and feeds them. The barn keeps your animals somewhere a thing can come and take them.
They are hunting us.
What it does
You keep a settlement under an open sky. Five kinds of bird come down for what you have. Three of them land to eat, one never lands at all and drops bombs on your roofs, and one picks up a villager and carries them off the map.
You answer with archers, and mostly with where you put things. There is no lane to block and no chokepoint to hold, so what you build decides which parts of the settlement have an arrow over them.
Arrows fly real arcs. A barracks raises archers with shortbows, and once you upgrade it you can raise crossbows instead: further, harder, at 1.5x the price.
A tower is the other answer, and it is a different kind of thing. One archer with a longbow, the longest reach in the game, standing where no beak can get at them. Nothing that lands can touch a tower archer, so the raid goes for the tower instead, and only when the building falls does the archer come down and become food. In practice you have bought an archer with enormous health.
Except from above. A bomb dropped on the tower kills the man on top of it, and the longbow up there is what reaches the bomber. Towers answer the landers, bombers answer towers, and the longbow answers bombers. That loop is most of the game.
Thirty-two hand-authored levels in eight chapters. The early ones teach one rule each. The later ones stop teaching. Behind them are two procedural seasons, twelve rounds and twenty-four.
Portrait, one thumb, and nothing moves until you press Start.
How we built it
No engine, no framework, no art assets. Vanilla TypeScript against a vendored three.js, shipped as one readable HTML file. Every bird, building, arrow and sound is generated at runtime from parameters. Nothing is loaded from a file, and once the page opens it makes no network requests.
We used Claude Code to have it developed for real, not prototyped, over about eighty long conversations with it. We set the architecture, made the design calls, and moderated the quality and the output. Once the core of the game existed most of the work became iteration: balancing it, and closing the gaps in the interface and in the play.
The moderating turned out to be the largest part of it, and not in the way we expected. The code was rarely bad. It was frequently untrue.
Challenges we ran into
We started blind. The agent was building the architecture we had asked for, and there was almost nothing we could check: no screen worth looking at yet, and no way to tell a system that worked from one that only reported working.
Early on it told us the initial version was done, and its own diagnostics backed that up. The draw calls were going out, the counters were right, and the screen was empty.
What fixed it was making it check the picture instead of the report. We had it read the actual pixels back off the screen and compare them between frames, rather than ask the renderer how it thought it was doing. Those are two different things, and only one of them can be wrong while sounding right. It took five more goes to get there.
That fixed the arrows. It did not fix the problem, which turned up again and again in different shapes.
Two levels could never end. A bomber never lands, so nothing but an arrow ends its visit, and on a level where every tower archer was dead it just circled. The round never cleared. The game was neither won nor lost. You sat and watched an empty sky.
Tests that proved things that had not happened. Every level ships with a scripted bot that must win it, and those plans were only checking that a move was legal, never that it worked. One level's plan spent 215 wood out of a 200 wood purse. The seventh building was silently refused, so the level was "proven winnable" by a settlement that was never built, and the suite was green the whole time.
Two levels printed "The village is gone" when you lost. On those two levels there is no bird in the script capable of harming a person. The game was announcing a massacre while every villager stood in the field.
The design document we submit with this said, in section four, that clearing the third level unlocks the endless mode. Section three of the same document said nothing is locked. Both were written by the same tool months apart, the game had changed in between, and only one of them had noticed.
There is a pattern there and it took us too long to name. An AI will tell you a thing is done. The thing it describes is not always the thing that exists. None of this was carelessness: the code and the claim about the code are two separate artifacts, and only one of them gets tested unless you go looking for the other.
Accomplishments that we're proud of
Every level carries two proofs: a bot that wins it, and an idle run that loses it. That sounds like testing hygiene and it is really a design rule. A level nobody can lose is not a level. A level nobody can win is a bug. One that hangs is worse than either. All thirty-two are held to it on every commit.
The other one is invisible. The architecture document's dependency map is generated from the code's own access declarations, so it cannot be wrong about them. After everything above, having one document that is structurally incapable of lying is the thing we are most pleased with.
What we learned
Ask the artifact, not the author. Every real bug in this project was found by measuring the thing itself: pixels off the framebuffer, the browser's own network log, the actual word count of the .docx rather than the markdown it came from. Every one was hidden by asking the layer that had just built it.
A test that cannot fail is worse than no test, because it reads as coverage. We shipped one demanding that "at least 8" of 28 systems be checked, which is always true. Another slipped through last week that could not fail either, and we only caught it by breaking the code on purpose to watch the test go red. It stayed green.
Measure the thing you assume is taste. The levels felt slow. Instead of arguing we measured the seconds of empty sky between one wave's last bird and the next wave's first. Some had 28 seconds of nothing. One had 38.
What's next for Birds Are Hunting Us!
The birds do not actually eat anything yet. A lander pecks the farm and damages the building, but nothing leaves your stores. Giving the three kinds three real crimes is the next change, so a bird that feeds and escapes takes what it fed on with it.
Then an endless mode that is actually endless, a way to resume a long level, and the thing no amount of testing here can replace: a pass on real phones rather than a browser pretending to be one.
Built With
- anthropic
- claude
- claude-code
- css
- esbuild
- firebase
- firebase-hosting
- glsl
- html5
- javascript
- localstorage
- node.js
- rollup
- three.js
- typescript
- web-audio-api
- webgl2
Log in or sign up for Devpost to join the conversation.