HomeArticlesGenerative Art

Procedural City Generation: Growing a Town From a Seed

Recursive block subdivision lays down roads and parcels, zoning gradients decide density, and one integer seed reproduces the whole city exactly.

mysimulator teamUpdated June 2026≈ 8 min read▶ Open the simulation

A city is a recursively subdivided rectangle

Strip away the buildings and a city plan is a tree. Start with one large rectangle — the whole map — and cut it in two with a straight road. Cut each half again, and again, and stop once a piece is small enough to be a single block. This is binary space partitioning, the same idea used to lay out dungeon floor plans and render pipelines, applied to city blocks instead. Every cut becomes a road segment, and every leaf of the tree becomes a buildable parcel that already touches a road on at least one edge — which is exactly the property random placement can never guarantee.

live demo · recursive block subdivision growing a road grid● LIVE

The cut orientation and offset are not fixed — they are drawn from a seeded random number generator, so the same seed always produces the same split at the same node of the tree. That single property is what makes procedural generation reproducible: a 32-bit integer is enough to regenerate an entire town, byte for byte, on a different machine.

Two families of road network

Real cities mix two very different generation logics, and so do their procedural models. A grid network — most North American downtowns, Barcelona's Eixample — comes from axis-aligned recursive subdivision: every cut is horizontal or vertical, producing rectangular blocks of fairly uniform size. An organic network — medieval European cores, hillside suburbs — comes from growing roads outward one segment at a time along the path of least resistance, closer to an L-system than a partition tree: each road tip extends, forks or stops based on local rules and a bit of noise, so no two branches look alike.

subdivide(rect, depth):
  if rect.area < MIN_BLOCK or depth > MAX_DEPTH:
      emit_parcel(rect); return
  axis = choose_axis(rect)              // prefers the longer side
  split = rect.size(axis) * jitter(0.35, 0.65)
  roadA, roadB = rect.cut(axis, split, ROAD_WIDTH)
  subdivide(roadA, depth + 1)
  subdivide(roadB, depth + 1)

Most convincing generators blend the two: an organic arterial skeleton is grown first between a handful of seed points, and the irregular cells that skeleton leaves behind are then filled with grid-style recursive subdivision — which is close to how many real cities actually grew, arteries first, infill later.

Zoning as a density field, not a rulebook

A city does not look uniform, and the generator should not treat it as one. A scalar downtown-intensity field — highest at the seed point, falling off with distance, perhaps with secondary bumps at transit nodes — drives three decisions at once: how deep the subdivision recurses (deeper near downtown means smaller blocks), what fraction of each block's footprint a building occupies (higher near downtown), and how tall the building on that parcel is allowed to grow. The result is a skyline that thins out from a dense core into low suburbs without a single hand-placed rule about where "downtown" is — it falls out of the field.

From footprint to building

Once a parcel exists, turning it into a building is comparatively simple: inset the parcel boundary by a small setback, extrude the resulting footprint upward by a height sampled from the local intensity field, and optionally split the extrusion into floors with a repeating façade texture. Some generators vary the footprint itself with a second, smaller subdivision — an L-shaped tower on a corner lot, a courtyard block downtown — but the height field alone already does most of the work of making a skyline read as a skyline rather than a field of identical boxes.

Why determinism matters more than realism

No procedural city generator is trying to reproduce an existing place block for block — the value is in infinite, on-demand, reproducible content. Because every random choice is drawn from a seeded stream keyed by position, a city can be generated in tiles on demand as a camera moves, without ever storing more than the seed and a few parameters, and any tile can be regenerated identically later. That is the same trick used by Minecraft's terrain and No Man's Sky's planets, applied to streets and skylines instead of caves and biomes.

Frequently asked questions

Is the generated city ever exactly the same twice?

Only if you reuse the same seed. The whole layout — road tree, block subdivision, zoning and building heights — comes from a single pseudo-random number stream, so one integer seed reproduces one city exactly. Change the seed and every downstream decision cascades into a different town.

Why do procedural cities usually look like a grid downtown and a maze at the edges?

That is a deliberate density gradient: near the seed point, subdivision is biased toward small, regular rectangular blocks because real downtowns are surveyed and regular; further out, larger and more irregular blocks are allowed to stand, mimicking how suburbs grow organically along whatever roads already exist.

Why not just place buildings at random instead of subdividing recursively?

Because random placement has no notion of a street network, so buildings would float in space or overlap. Recursive subdivision guarantees every parcel it produces already borders a road on at least one side, which is what makes the result buildable and walkable rather than just decorative noise.

Try it live

Everything above runs in your browser — open Generative City and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.

▶ Open Generative City simulation

What did you find?

Add reproduction steps (optional)