Space, Time, and Knowledge: Modeling a World as One Graph
How a character creator turned into a world engine, and the three-axis model that made almost every feature after that a question of where a new entity sits rather than what new system to build.
I set out to build a D&D character creator. It works: both rule editions, guided level ups, character branching, PDF export that round trips back into the app. That was the whole scope.
Then I asked a question I could not put down. If I can generate a character, why can I not generate the world they came from?
Two months later I had a world engine, and the thing that kept it from collapsing into spaghetti was a model I found early and then refused to break.
The problem with feature-shaped thinking
The obvious way to build this is one system per noun. A map system. A timeline system. A lore wiki. An NPC manager. A faction tracker. A campaign manager. A combat layer.
That is how the existing tools in this space are built, and it is why using them together is miserable. Your map does not know your timeline. Your wiki does not know your map. Your combat tool does not know any of it. Seven systems means twenty-one integrations you will never write.
I got about three systems in before it was obviously wrong.
Three axes
What I landed on is that everything in the product is an entity, and every entity sits on three axes.
Space. A node tree: world, region, settlement, interior, battlemap. Any entity can be anchored to a node, and optionally to a coordinate inside it. A tavern, an NPC, a piece of lore, a battle, and a map pin are all just things with a position in that tree.
Time. A change log. World state is a seed plus an ordered list of operations, and named eras are bookmarks over that list rather than a separate concept. Any entity can carry a time anchor, which means the entire world is replayable as of any moment. Not a snapshot history. A replay.
Knowledge. Visibility crossed with scope: DM only, hidden, shared, or targeted at specific people, at either world or campaign level. This is the axis the other tools miss entirely. It is fog of war over information as well as over terrain, and in a game where the whole point is that the players know less than the DM, it is not an access control feature. It is a core modeling axis.
Why this was the unlock
Once those three exist, the map is the spatial view of the graph. The timeline is the temporal view. Each player's hub is the knowledge-filtered view. They are not three systems that need integrating. They are three projections of one thing.
And every subsequent feature stopped being "what system do I build" and became "where does this entity sit on the three axes."
A faction is an entity with a region in space, a founding in time, and usually partial visibility. An item is an entity that moves through space over time and is often hidden. A quest is an entity anchored to several others. A generated innkeeper is an entity at room-grain space, present in the current era, visible once the party walks in.
None of those needed a new subsystem. They needed a kind and some typed edges.
The rule that keeps it honest
One entity core, one edge table. Everything with a kind, a name, lore, a space anchor, a time anchor, visibility, scope, and provenance lives in the same core, with heavy per-kind detail hanging off as a thin side table. Relations are typed edges in one place.
The temptation, every single time, is to give a new kind its own table because it "does not really fit." Every time I have given in to that instinct on other projects, I have ended up writing the same query five times against five schemas, and then writing a sixth thing to reconcile them.
There is one deliberate exception. Map annotations like pins and labels stay concrete tables, because they are not graph-connected and forcing them into the entity core would buy nothing. An exception you can justify in one sentence is fine. It is the ones you cannot that eat the model.
What it looks like when it works
The proof that the model was right is the features I got for free.
The era system was built to let a DM say "show me the world in the Third Age." When I later generated procedural history and wrote it into the same change log, the scrubber already worked on it, because the ancient world is just more operations. Rename operations replayed through the log produce "previously known as" without anybody implementing that feature.
Visibility was built for hiding DM notes. It now filters generated inhabitants, faction attitudes, and history events, because those are entities too, and entities have a knowledge axis.
That is the tell for a good model. Not that it handles what you designed it for, but that the features you had not thought of yet keep landing in it for nothing.