BetterSWE DigestWhat is BetterSWE?

Deep Dive · Issue 7

PGSimCity: What Building a City-Builder Taught Us About Postgres Internals

Postgres will tell you which page it evicted, never how the clock hand got there. So someone built a 3D city to show the how.

A 3D city with districts labeled as Postgres subsystems

The problem

The thing Postgres won't show you

A 3D city with districts labeled as Postgres subsystems

Say you want to see how Postgres picks a page to evict from shared_buffers. The engine will happily tell you which frame it chose, but the frame-by-frame sweep of the clock hand that got there is invisible. Postgres only exposes catalogs, pg_stat_ views, and EXPLAIN, and none of those show the internal steps.

PGSimCity answers that gap with a 3D city you can walk through, where every district is a piece of the engine. It ships as a single static browser bundle with three.js as the only runtime dependency, no server, no database, and no network calls. And it is explicit that no PostgreSQL source runs in the browser: it is a model, not an emulator.

The core tradeoff

A model, not an emulator

A sealed engine next to a transparent model, joined by a SimState contract

Why not just run the real engine? Projects like PGlite compile actual PostgreSQL to WebAssembly, which makes query results and plans authoritative. The catch is that a real engine can only expose what a real engine exposes: catalogs, pg_stat_ views, EXPLAIN. A hand-written model can show the internal mechanism but risks being wrong. As the author puts it, the two approaches answer different questions.

To keep the model honest, the codebase enforces a hard boundary. The simulation is pure TypeScript and never imports three.js. The rendering world may read the shared SimState but never mutates it. They meet only at that one contract. It mirrors the real separation in Postgres between the engine and its observability surface.

sim (owns the truth)SimState (the contract)world (reads, never writes)

That one rule buys a lot. Because the simulation is deterministic pure math with a seeded RNG, it can be tested without a browser or a GPU in the loop. The city is just one reading of that state, so a rendering bug can never quietly corrupt the mechanism it is trying to show.

A mechanism up close

1024 buffers and a spinning clock hand

A plaza of glowing page frames with a rotating clock hand

The city center is the buffer pool, modeled as 1024 page frames standing in for a real pool of a million, with one particle representing thousands of tuples. The numbers are deliberately scaled down so a human eye can actually track them. Each frame's height is its clock-sweep usage count, and its color is the true buffer state.

The interactive part is where it earns its keep. Drag shared_buffers down to 64 pages and the plaza visibly thrashes: usage counts collapse, the clock hand accelerates, and backends start writing out their own dirty pages because nothing clean is left to evict. That last part models a real behavior: when BufferAlloc cannot find a clean victim fast enough, the backend does the write itself.

        clock hand ↻
           │
  [▓][ ][▓][ ][ ][▓] ... 1024 frames
   height = usage count

  shared_buffers → 64
   ├─ usage counts collapse
   ├─ hand spins faster
   └─ backends write dirty pages themselves
Shrink the pool and the eviction path changes on screen

This is exactly the kind of cause and effect the pg_stat views can only hint at after the fact. You see the pressure build and the fallback trigger in the same frame.

Design as information

Structure is matte, meaning is neon

A dark matte city where only meaningful elements glow in neon

The visual language carries a rule of its own: structure is matte, meaning is neon. Only emissive materials cross the bloom threshold, so anything that glows is carrying information rather than decoration. The buildings are context; the light is data.

Color is semantic and fixed per mechanism, so a subsystem reads the same everywhere in the city. Once you learn the palette, you can glance across the skyline and tell what is happening without a single label.

  amber   → WAL            blue  → clean pages
  red     → dirty pages    violet→ vacuum
  pink    → checkpoints    teal  → bgwriter
  orange  → replication    green → storage
  aqua    → indexes        red   → locks
A fixed, semantic palette per mechanism

It sounds like styling, but it is closer to a type system for the eye. A glowing amber ribbon is always WAL, so seeing it pool or drain tells you something real about durability pressure, not just that the scene looks busy.

What building it taught

Geometry is a factual claim

A horizon plane floating wrongly above worker figures with a failing test marker

The sharpest lesson from building the thing: a picture of an internal mechanism can be wrong the same way a sentence can. The xmin horizon, the oldest transaction still visible, once floated above every active backend even after two prose reviews passed. In Postgres that horizon is what pins dead tuples in place and blocks vacuum from reclaiming them, so a scene that draws it in the wrong spot is telling a lie about MVCC.

That failure produced a discipline. Every bug fix now starts with the smallest deterministic test that reproduces it, and the test must assert behavior, not that a symbol merely exists, since presence is not proof of being wired up correctly. Geometry, animation, metrics, and prose all get the same technical review, because a caption cannot correct a misleading building.

A caption cannot correct a misleading building.

The deeper takeaway is that teaching a system honestly forces you to encode its invariants somewhere concrete. Here they live in tests and in the fixed meaning of every coordinate and color, not in trust that the author drew it right.

The takeaway

Two ways to be truthful about a database

Why it matters

PGSimCity draws a clean line between two kinds of truth about a system: an emulator that runs the real engine and is authoritative about results, and a model that can show the internal steps the engine never exposes. Postgres beginners usually hit MVCC, vacuum, and buffer eviction as opaque folklore; a scaled-down, observable city turns those into something you can watch respond to a knob. It is a v0.1 prototype that may contain inaccuracies, so treat it as a teaching lens, not a reference.

The short version

  • PGSimCity is a **model, not an emulator**: no Postgres runs in the browser, so it can show internal steps like the clock sweep's victim choice.
  • A running engine (PGlite/WASM) is authoritative but only exposes catalogs, `pg_stat_` views, and `EXPLAIN`, never the internal mechanism.
  • An architectural wall keeps the model honest: the **simulation never imports three.js**, the render world never mutates state, they meet at `SimState`.
  • Scaled numbers make internals watchable: **1024 frames** for a million, and dragging `shared_buffers` to **64** makes the pool thrash on screen.
  • The hardest lesson: **geometry is a factual claim**, so a wrongly drawn `xmin` horizon is a bug that prose review cannot catch.

Sources

  1. 1.GitHub - NikolayS/PGSimCity: An explorable 3D city that shows how Postgres actually works · GitHub
  2. 3.PGSimCity · How PostgreSQL Works, in 3D
  3. 6.PGSimCity/CLAUDE.md at main · NikolayS/PGSimCity · GitHub
  4. 12.Why we need VACUUM to implement MVCC In PostgreSQL | EDB