KINDLEFALL Play free
Part 6

What made it good

The lessons, for the next game.

A lot of games built quickly feel built quickly. We wanted to work out why Kindlefall mostly doesn't, and it isn't one thing. It's a handful of habits, most of which were written down early and then followed every time. Here they are, for the next game.

A Photo Mode shot of the Forge Heart: two enormous cracked molten arms reach out of a lava pool toward the arena floor, lit orange from below
Photo ModePress O in play: the world freezes, the HUD hides and the camera flies free. Every still in this post was taken that way.

1. Write the memory down

An AI session doesn't remember yesterday. AGENTS.md does. It starts with a rule ("every change you make gets a line in the Changelog, and any new knowledge goes into the relevant section"), then the run instructions, a map of every source file, the performance budgets, the rendering gotchas, the audio design notes, and a changelog entry for every build. Every session, every agent, every specialist team read it first.

The part that paid off most was the gotchas section: things we learned the hard way, written so nobody learns them twice. Chrome caching ES modules. Metal returning NaN from pow(0, y). Never resizing the renderer after rendering in the same frame. Changing the number of lights recompiles every shader. When a later agent hit one of these, the answer was already in the file it had just read.

2. Put numbers on "feel"

Early on the AI wrote docs/PLAYBOOK.md: the house standards for game feel, with the real numbers from the code. Every event that matters belongs to a tier, and gets all the channels in its tier:

TierExampleHit-stopShakeRumble
LightSword hit, enemy death0.05 s0.18–0.200.25, 90 ms
HeavyCombo finisher, Emberbolt blast0.08–0.1 s0.40.6, 160–180 ms
MassiveBoss slam0.08 s0.81.0, 300 ms
CinematicBoss intro, boss deathslow-motion instead0.6–1.01.0

Plus flash light, particles, sound and HUD feedback per tier. "Missing channels are what make a new feature feel cheap." That one sentence is why a boon added overnight by an agent that had never held a controller is still expected to land with its own flash, ring and sound: the recipe for a new boon says so.

The same document sets the rules that make fights readable. Telegraph everything that can hurt the player, at least 0.45 seconds ahead: a body flash and a sound, a ground decal that fills up, or an aim line. A colour language: orange and gold are yours, red is danger, violet is enemy magic, cyan is souls, green is healing, cold blue is the Hollow. And brightness discipline, because big additive shapes blow out through bloom into a white mess. "Always look at a screenshot of the effect at its busiest moment."

Readable in the darkThe Lightless Crypt on Floor III. Fire reveals wraiths; lit lanterns push the dark back.

3. Keep the loop short and honest

The single most important thing was how fast a note became a build. Play in the evening, write a few lines, and play the result the same night. One playtest day shipped seven builds. That only works if both sides know exactly what's being tested, which is why the build stamp is on the title screen, why stale code shows a red banner, and why every build gets a changelog entry in plain words.

The other half of honest is admitting what the tests can't see. Every hand-back ends with a list of things for a person to judge. No agent has ever heard the game; there's a line in AGENTS.md that just says sound quality has to be judged by a person.

4. Let a dumb bot be the referee

The test bot is ninety lines and can't beat a shield wall. It doesn't need to. It needs to play every floor end to end, with every hero, and scream when something is wrong: a console error, a boss in an invalid state, a room that never clears, a frame over budget. With that in place, agents could prove their own work, and the orchestrator could run the whole battery before every ship. The Sprint #2 brief still says it: run the tests, and "look at your screenshots before reporting".

And never take "done" on faith. The orchestrator sent work back twice because the screenshot didn't match the report.

5. Make it safe to work in parallel

The overnight sprint worked because, before any agent was spawned, the codebase got seams: a registry, hooks, content modules, a written contract. Teams owned slices (a floor, the boons, the heroes) and kept them across rounds, and only the integrator touched the core or shipped. "If two agents would need the same file, add a hook or registry instead." Next time, that comes first, before the first feature.

6. Use constraints as a style

Some of the best-looking things in the game came from rules that sounded limiting:

Photo Mode shot of the Hollow Barrows: the knight among blue-lit gravestones glowing violet, with warm lanterns and fogIII · cold fire
Photo Mode shot of the Hearth of Origins: a glowing halo over a dark obsidian floor veined with gold as beams of light strike the groundVII · black glass
One identity per floorCold blue against warm lanterns; black obsidian veined with gold.

7. Treat performance as a feature

The development MacBook ran hot early on, so the game caps itself at 60 fps by default (the Mac display runs at 120 Hz and uncapped meant double the GPU work), renders High quality at 1.5x instead of full Retina, and draws menus at 30 fps. The whole dungeon is about 10 to 20 draw calls of merged chunks; the bot's test runs fail any fight over 90.

Two fixes every project should know about. A "performance hit" turned out to be shader recompiles: three.js frees a compiled program when the last material using it is disposed, so every effect that came and went meant a fresh compile and a 4 to 30 ms hitch every few seconds of combat. Pinning programs for the session fixed it. And the first-visit stutter on every floor, up to 74 ms, came from warming up shaders for the canvas when the game draws into an HDR target; warming the right target, and pre-rendering each floor once on the title, brought the worst frame down to 9.6 ms. Memory got the same care: music loads per floor (it was about 400 MB decoded at once), and the sound-effect sprite decodes mono at 32 kHz (325 MB down to 110).

Measured onMacBook Pro, Apple M3 Max, Chrome with Metal, High quality, 60 fps cap. The music and sound-sprite memory figures are calculated from the audio formats, not measured on a machine. More in part 9.

8. Debug the state machine, not the symptom

The first real bug was "the boss gets stuck". Pathing tests couldn't reproduce it, because it wasn't pathing: the Bone Tyrant's attack picker could return undefined when you were far away twice in a row, and he'd sit in an unknown state forever. The lesson in the docs: "when a bug report can't be reproduced by pathing tests, look for state machines that can enter an invalid state." Every state machine in the game now has a default: that falls back to a safe state, and the boss refuses to begin an attack it doesn't know.

9. Let the player own the taste

Looking back over the changelog, the decisions that most define how Kindlefall feels were small calls made while playing: hide boon rarity, keep the two-press confirm, slow the opening down, earn ripostes instead of mashing them, end on a single word instead of a credits roll, keep the long roar for bosses, keep the glare down. None of those are things a test can tell you. The AI's job was to make each one real by the evening. The playtester's job was to notice.

What we'd change

From the team playbook, written after the overnight sprint and the playtest rounds:

And one of our own: keep old builds runnable. Every "before" picture in these posts was made by pulling an old commit out of git, serving it, and letting the same bot play it.

Photo Mode shot of the Mother of Tides' pale hand pressing toward the floodwater, a gold ring of light marking where it lands
The Drowned CathedralPhoto Mode, mid-fight.
Play Kindlefall

In your browser, with a keyboard and mouse or a controller.