About $95 a month to watch deer starve
I built a thing called Wildwood: an isometric forest you watch rather than play. No controls beyond a camera. Deer grazed, walked to water, formed loose leaderless herds and raised fawns. Wolves lived in family packs of four to twelve, led by the oldest animal, held territory, hunted the deer, fed together at a carcass, and fought rival leaders at the borders.
Nothing was scripted. Each animal ran a small state machine over its own hunger, age and neighbours. Everything at the population level, herds drifting, packs splitting, booms and crashes, was whatever fell out of those rules. One authoritative simulation ticked 24 hours a day on AWS, so everyone who opened the page saw the same animals in the same places at the same moment.
I switched it off last week. Here is what it cost, and what it taught me.
A small world cannot hold a predator and its prey
The first map was 24x24 tiles: fourteen deer, six wolves. It never once found a stable balance. Every tuning attempt produced the same two failure modes on a loop. Give the wolves enough reach and they overhunt, crash the deer, then starve. Take reach away and they starve anyway, while the herd, which always has a refuge somewhere, grows unchecked.
I swept the parameters exhaustively. On a map that small, coexistence was not a tuning problem at all.
What fixed it was not a better number. It was space. Scaling to 96x96, sixteen times the area, with every population and terrain feature scaled to keep density constant, changed the behaviour qualitatively. A big map does not hold one population. It holds many partly independent sub-populations that fall out of sync with each other, so when wolves crash the deer in one corner, deer from a distant corner wander back and repopulate it. Ecologists call this the metapopulation rescue effect. It is the difference between a system that oscillates and one that dies.
I had spent days tuning escape probabilities on a map that was structurally incapable of supporting the answer.
Hunting is cheap. Eating is the hard part
For a long stretch every wolf pack in every random seed died out within sixteen in-game months. The cause was not the hunt. When a pack made a kill the whole pack ran to share it, hunger kept draining during that run, and the monthly starvation check killed the stragglers a few tiles short of the carcass. Every single wolf starvation death was happening to a wolf already committed to a meal. Exempting a wolf en route to a kill flipped the species from reliably extinct to reliably surviving. One conditional.
Then there was my favourite bug. A feeding wolf is exempt from starvation, so a wolf that could not physically reach an awkwardly placed carcass would freeze in place, permanently, and being technically mid-meal it could never die. These zombie wolves padded the population count and hid the fact that the pack could not sustain itself. Removing the freeze made the ecosystem look dramatically worse, because I was finally measuring it honestly.
A bug that props up a number is worse than a bug that breaks one. Every balance conclusion I drew during those weeks was wrong.
Then it died anyway
The tuned, verified answer held for the fifteen simulated years my sweeps covered. The live world ran for 443.
Over a long enough run the predators went extinct regardless, and the deer expanded until grass, not wolves, was the thing limiting them. In the final twelve in-game months: 94 starvation deaths, 4 of old age, zero predation. The herd settled into a population held down by hunger at the carrying capacity of the map. Nature’s least dignified equilibrium, and a real one.
A balance that survives fifteen years and fails at four hundred is not a balance. It is a slow transient. Only running the thing continuously for weeks showed me the difference.
The bill was the real lesson
The server broadcast the full world state ten times a second to every connected viewer. This is the easy, correct-by-construction design: no delta logic, no reconciliation bugs, and every browser is a dumb renderer. It is also free on localhost and ruinous the moment it crosses a metered load balancer.
In its final full month, that load balancer moved 715 GB.
Data transfer alone cost more than the compute, database, registry and DNS combined. Before I looked properly I would have guessed the whole thing ran at about $45, because the egress line simply was not in my mental model. A single browser tab left open on the site for a month generates on the order of a terabyte.
The uncomfortable part is that nothing was wrong. The traffic was not a bug, a leak or an attack. It was the design working exactly as intended, for an audience of nobody, at 10 Hz, forever.
Design your wire format assuming someone leaves the tab open for eternity. Full-state broadcast scales its cost with viewer-seconds, not with activity. Deltas were an optimisation I always meant to get to. The invoice got there first.
Wildwood ran from 7 June to 27 July 2026: seven weeks of wall time, 443 in-game years, about 4.7 million ticks. The domain now serves a static retrospective for a few cents a month, and the simulation still runs fine on my laptop, which is where an experiment like this probably belonged all along.
The full write-up, with more on deer behaviour, isometric depth sorting and the sprite-rendering bug that made every deer walk sideways, is at ecosystem.valentevidal.com.
