A ruled ink diagram of an idealised plant pinned on a drafting table beside a real pressed specimen that grew crooked and low, in warm raking light, a dish of gold leaf at the edge.

You Can’t Reason Your Way to a Selection Pressure

/ Maxim Starkweather / 9 min read

A companion to SEDIMENT, a small falling-sand world you can sow yourself. Every figure below is a real measurement from the build; the notes at the foot say where each one came from.

SEDIMENT (demo.temperaturezero.com/sediment). Every figure below is a real measurement from the build; see the notes at the foot for where each came from.*

I gave the plants in a small world a genome this week, and for most of a day they evolved in the wrong direction.

The world is called SEDIMENT. It is a falling-sand simulation — sand, water, fire, stone, gold — and the plants in it already had a life: a seed beds into wet soil, sends up a stalk, puts down roots, flowers at the tip, throws a seed to the wind, and dies back to ash that beds the next one. What it did not have was descent. Every plant was the same plant.

The SEDIMENT interface at day 301 — a pixel world of green stalks, pale pools and a dark seam of bedrock, with the world's chronicle of events listed in a column alongside it.
SEDIMENT at day 301 of a sown world. The chronicle on the right is the world narrating its own history — every line of it triggered by a measurement, not a script.

So I gave each living cell one byte. Four traits, two bits apiece: how tall the strain grows, how readily it branches, how far from water it can live, and a tint, which does nothing at all except make a lineage something you can watch spreading rather than a number in a save file. Inheritance runs through germination, growth, branching, flowering and the seed the flower throws, and it can slip once, at seed-set, by a single notch.

The point of the exercise was the third trait. Water reach was supposed to be the lever the whole thing turned on: the shore is comfortable and crowded, the inland ground is empty and dry, and a strain that drifts toward drought tolerance should be the only thing that can take it. That is the story. It is a good story. I have read it in textbooks.

It produced the exact opposite, and it took me three tries to find out why.

The first way to be wrong

I built it the obvious way. A seed will only bed in if there is open water within its own genome’s reach. Inland ground is therefore reachable only by a strain that has drifted, which is precisely the sentence I wanted to be true.

Mean water reach fell. Not in one region — in all of them, steadily, away from the wild type and toward the thirstiest strain available. By day 200 the short-reach genome had gone from nothing to several hundred stalks and was still climbing, and the strains that could have taken the dry ground had never established anywhere.

The reason is not subtle once you see it, and I did not see it. The frontier is a sliver of a stand. Nearly every plant in that world lives where water is plentiful, and in that crowd a drought-tolerant strain is not a specialist, it is just a slower grower carrying a cost for a benefit it never collects. Whatever the handful of plants out at the edge were selecting for was swamped, every generation, by seed blowing in off the shore.

Selection that only applies at a margin is not selection. It is a rounding error with a narrative attached.

What fixed it was making the absence of water fatal rather than merely inconvenient. A plant now holds only ground it can drink on; past its own reach it dies back from the tip far faster than age would take it. That single change moved the pressure off the frontier and onto every plant in the world at once, and the cline appeared almost immediately: at the water’s edge the mean sits around one notch, and out on ground seven or more cells dry it sits between two and three, with the short-reach strains simply absent.

The second way to be wrong

With that working, I let it run for four hundred days and watched the world turn into a wall of green.

Height and branchiness had no price. Both are pure wins — a taller stalk has more tips, more tips means more flowers, more flowers means more seed — and by day 300 every region had run to the tallest, branchiest genome available and stayed there. The pools were invisible behind the growth. The number of living cells was still climbing at day 400 with no sign of a ceiling.

This is the most boring lesson of the three and probably the most transferable. A trait that costs nothing fixes at its maximum. Not eventually, not on average — reliably, and faster than you expect, because there is nothing to hold it back.

The fix was to make size expensive in the currency that was already scarce. Both traits now cost cells of water reach, so a tall branchy plant is a thirsty one. That put the big thickets where the water is and left low scrub everywhere else, which is a landscape rather than a lawn, and it did it without a single rule about where anything was allowed to grow.

The third way to be wrong

The third one is the one I would not have found by thinking, and I want to be precise about it because it is the whole argument.

The price of drought tolerance was supposed to be growth rate: a tolerant strain grows slowly, so it reaches flowering height later, so it makes less seed in a life. I had written the constant. I had commented it. I believed it.

But a plant could flower once it stood six cells tall, and every strain in the world reaches six cells within a couple of hundred ticks of a life that lasts four and a half thousand. So the fast grower flowered from about tick 190 and the slow one from about tick 790, and against a lifespan that long the difference in lifetime seed came to roughly a seventh. The cost was real, it was in the code, it was doing almost nothing.

I changed one condition — a tip may open only once it has finished growing, at its genome’s full height — and the same constant went from moving reproduction by a seventh to moving it roughly threefold. The fast strain now reaches its cap around tick 750 and spends the rest of its life flowering; the tolerant one is still growing at tick 3,160. Nothing else changed. The shoreline became worth winning, and the whole cline stopped wandering.

What actually found these

Not thinking. I had a story for each design before I ran it, and each story was plausible, internally consistent, and wrong. Plausibility is not evidence, and the more coherent the account, the longer it survives contact with a result that should have killed it.

What found them was dull. A harness that prints the mean trait value in each third of the world once an in-game day. A script that runs the world headless, screenshots it at intervals and lays the frames out as a contact sheet with the census printed underneath. That second one exists because of a bug from an earlier round, where every stalk in the world hung floating in mid-air and every number I was watching looked perfectly healthy.

Six captures of the same simulated world stacked in pairs, from a sparse day 24 to a low crowded day 300, each frame captioned underneath with its day and its count of living things.
The same world at six moments, days 24 to 300. Printing the number caught the drift; drawing the picture caught the stalks that were hanging in mid-air while every number looked healthy.

The same discipline caught two things that were not evolution at all. Sand in that world went from 447 cells to 2,803 over two hundred days while stone stayed flat, which meant the sand was not coming from erosion — the forest was quietly laundering its own dead leaves into permanent ground, through roots that grew through ash and died back as sand. Left alone it would have buried the pools the whole world depends on. And the pools themselves were dissolving: two hundred and five cells of water that started as two ponds across thirty-eight columns had spread into a film across ninety by day 450, at which point no ground anywhere in the world was out of reach of a puddle and the entire premise quietly stopped existing.

Both were invisible in the metrics I was watching. The ash check passed the whole time. It passed because the ash was turning into sand.

The size of the claim

I want to be careful here, because this is the point where a piece like this usually reaches for something it hasn’t earned.

This is a toy. Four traits of two bits each, one real selection pressure, sixteen thousand cells, a genome that fits in a byte. It is not a model of evolution and it is not a model of anything else. Nobody should draw a conclusion about biology from it, and I am not drawing one about machine learning either.

What I will say is narrower and I think survives: I designed the incentive three times, and three times the pressure I specified and the pressure I actually built were different objects. Not subtly different — opposite, in the first case. And the gap was never visible in the design. It was only ever visible in the measurement, and only then because I happened to be printing the right number.

If that is the failure rate on a system this small, with a mechanism this legible and a designer who wrote every line of it, my bet is that the gap does not get smaller as the system gets bigger. It gets harder to see. The thing that scales is not the reasoning. It is the habit of printing the number and drawing the picture, and of believing them over the story.

The world is live and it starts empty. Sow a shore and leave it running at ×32 for a minute, and the chronicle will start telling you which way its plants have gone.

Three hundred in-game days in twenty seconds, captured from the build that is live. The world starts as bare ground and two pools; everything after that is the simulation running.

Notes on the figures

All measurements are from the build session on 2026-08-08, taken with qa/harness.mjs and qa/probe.mjs in the SEDIMENT repo.

  • Reach drifting downward under germination-gating: mean notch fell below the wild type in all three regions; short-reach stalks rose from 0 to 587 of ~3,000 living cells by day 200.
  • Traits fixing at maximum: mean height notch 2.0–2.7 and branchiness 1.0–2.5 by day 300–400; 5,059 living cells at day 401 and still rising.
  • Flowering threshold: FLOWER_MIN_H = 6 cells against PLANT_LIFESPAN = 4,500 ticks; growth-rate multipliers 1.6× and 0.38× either side of wild type. Flowering windows before the change ~4,312 vs ~3,710 ticks; after, ~3,750 vs ~1,340.
  • The cline, and how long it lasts: mean reach notch ~1.07 on ground within three cells of water against 2.0–2.8 on ground eight to twelve cells out, with short-reach strains under 5% of what stands on the dry. That is a young world. A 300-day re-run says the gradient does not survive the world filling in: day 26 reads 1.11 at the shore against 2.00 on the dry, day 151 reads 1.80 against 1.38, and by day 301 all three bands sit between 2.21 and 2.33, the short-reach strains are extinct, and only 86 of 2,537 living cells are still standing on ground eight or more cells dry. That is the second failure mode again at the scale of the whole world: once the stand is continuous there is no dry ground left to pay for reach on, and the trait fixes at its maximum. I did not design that one either, and I do not have a fix for it yet.
  • Sand accumulation: 447 → 2,803 cells over 200 sown days, stone flat at 1,381.
  • Pool dissolution: 205 water cells across 38 columns at genesis, 90 columns by day 450, zero columns beyond the reach of any genome.
  • Performance, for the curious: a tick over 16,000 cells costs 0.466 ms with a grown-in forest — 6.4 ns per cell to walk the grid, ~155 ns per living cell.
A ruled ink diagram of an idealised plant pinned on a drafting table beside a real pressed specimen that grew crooked and low, in warm raking light, a dish of gold leaf at the edge.

AI-generated editorial illustration · TemperatureZero · August 9, 2026

Keep reading the signal

Get the Daily Signal — a concise briefing on what actually matters in AI and the systems around it.

Subscribe Free

Continue the archive

Latest BriefingsArticlesAbout Temperature Zero