Generating a Thousand Years of History, and the People Living in It
Procedural history written as a prefix on an existing change log, and a population that is derived on demand rather than stored. Two problems that turned out to have the same shape.
There are two questions that separate a generated world from a living one.
Why is that kingdom at war with its neighbor? And who is behind the bar in this tavern?
Standard procedural generation answers neither. It produces a coherent now with no explanation: states exist, borders exist, towns exist, and none of it has a reason. Walk into a building and it is empty, because generating every person in every building is obviously impossible.
Both of those turned out to have the same solution, and I did not see it until I had built the first one.
History as a prefix
The world engine already had a change log. World state is a seed plus an ordered list of operations, and eras are bookmarks over that list. It was built so a DM could edit the world over a campaign and later ask what things looked like six months ago.
The insight, which felt obvious about ten seconds after it stopped feeling impossible, is that generated history is just operations placed in front of the ones you already have.
The freshly generated world moves to position zero and becomes the ancient world. History gets written forward from there, using the exact operation vocabulary that already existed for hand edits:
- A town founded or razed is an add or remove
- A conquest that renames a city is a rename
- A war is an annex or a transfer of territory
- A wonder raised and later lost is a pair of landmark operations
- The cataclysm that split the valley is a terrain nudge
Not one new primitive. The log did not need to learn about history, because history is not a different kind of thing from a DM moving a town. It is the same kind of thing, further back.
The payoff was immediate and slightly absurd. Everything built to make the present visible worked on the past for free. The era scrubber walks the ancient world because it walks operations and does not care where they came from. Rename operations replayed through the log already derived "previously known as," so cities acquired their own former names with nobody implementing that.
No simulation, and no model
Two things history is emphatically not here.
It is not a simulation. There is no economy, no army math, no agents with goals. History is authored by weighted programs with causal constraints, which means a conquest cannot happen between kingdoms that never shared a border, and a city cannot be razed before it was founded. Believability comes from the constraints, not from simulation depth. Simulating four hundred years of politics to decide two facts a DM will actually use is the wrong trade, and simulations drift into nonsense in ways that are very hard to bound.
It calls no language model. History is where the temptation is strongest, because prose generation is exactly what models are good at. But a generated past has to be reproducible from a seed, internally consistent, and identical on every machine, and a model gives you none of those. Prose is assembled from templates and the culture-aware naming that already existed. It is less florid than a model would write and it is true to itself, which matters more.
The population problem
Then the same shape showed up again.
The goal: a party walks into any building and finds someone there. The DM talks to them. The same innkeeper is behind the bar next month.
Storing that is impossible. A city has thousands of people, a world has hundreds of settlements, and almost none of them will ever be spoken to.
So do not store them. Population is a deterministic read model. Given a settlement and a seed, the demographic mix is computed, and given a building, its occupants are derived. The innkeeper is not a row. She is a function of where she is standing.
She becomes a row the moment the DM touches her. Name her, give her a secret, put her in a quest, and she is written to the database as a durable entity that survives unrelated edits to the world around her.
Derive by default, persist on contact. The database holds only the parts of the world that someone cared about, which is a tiny fraction, and everything else is still there whenever anybody looks.
That same demographic model then feeds character creation, so a player can roll a character who plausibly comes from the town they are about to play in. One read model, two features, no new storage.
The pattern
Both problems looked like "generate and store an enormous amount of content." Neither was.
History was not new content, it was existing content extended backward through machinery that already existed. Population was not content at all, it was a function that occasionally gets memoized by a human being paying attention to it.
The general version, which I keep finding in other projects: before you build a system to store something, check whether you can compute it instead, and store only what somebody touched. The storage you avoid is not the point. The point is that everything derived stays consistent with the thing it derives from, automatically, forever.