Traluno▶ Back to the layout

Building a Town That Does Not Repeat

Twelve houses from one model, and none of them look like the same house twice. The trick is not more models — it is deriving the variation from where the building stands.

A model railway town is mostly the same building several times. That is true of real suburbs too, so it is not automatically wrong. It becomes wrong the moment the eye notices, and the eye notices instantly, because identical objects in a row are one of the things human vision is best at.

Traluno has a handful of house models and towns of thirty or forty buildings. They do not read as repeats. Here is the mechanism.

Derive the variation from the position

The naive approach is a random number per building, rolled when it is placed. It works and it is a trap, because random means the house changes every time the layout is loaded. Save, reload, and the blue house is now yellow. That is unsettling in a way that is hard to name and impossible to ignore — the town stops being a place you built and becomes a place that is generated near you.

The fix is to hash the position instead. Take the tile coordinates, run them through a cheap integer hash, and use the result to pick from the variation ranges. The same house on the same tile is the same house forever, across sessions and devices, without storing a single extra byte in the save file.

Move it one tile and it becomes a different house. That is a feature, not a flaw: nudging a building until it looks right is a real thing modellers do, and here it is also how you reroll the paint.

What varies

Not shape. Shape variation is expensive to author and, oddly, less effective than you would think — two houses of different silhouette next to each other read as two different buildings, which is fine but does not fix a row of twelve.

What varies is:

Base colour, across a fairly wide range within a palette that suits the region. Not free hue, which produces a clown town, but a spread within a family.

Roof colour, chosen semi-independently. Semi, because a red roof on a red house is a specific look and should be rare rather than common.

Trim and door colour, from a small set. This one carries more weight than its size suggests, because trim is what your eye uses to tell two similar buildings apart at a distance.

Storey count, where the model supports it. One extra floor changes the outline enough to break a row without needing a second model.

Rotation, in ninety-degree steps. Free, and surprisingly effective, because it moves the door and windows to a different face.

The per-cube jitter underneath

There is a second layer of variation that runs at a much finer grain, and it is doing more work than the per-building variation.

Every cube in a built model gets its colour nudged very slightly from the model's base colour. The nudge is tiny — a few percent of brightness — and derived from the cube's own position, so it is stable in the same way.

The result is that a wall is not a flat colour. It is a field of very slightly different colours, which is what a wall of painted bricks actually is, and which makes the cube seams read as construction rather than as an artefact of the renderer.

Turn it off and everything goes plastic instantly. It is one line of code and it is probably the highest return on effort in the whole art pipeline.

Where it deliberately does not apply

Manufactured objects get no jitter, because a factory-painted surface is uniform and jittering it makes it look weathered when it should look new.

More interestingly, the glTF vehicles and figures get no colour variation at all, and cannot. Their colours are baked into a shared texture atlas, so tinting the material shifts the whole model — hair, skin, clothes and all — rather than just the paint. A tinted crowd is a crowd of monochrome people.

So variety for those comes from a different mechanism: each entry in the asset manifest can list several files, and the game picks between them. Five pedestrian files give five different people, and that is the whole range. It is coarser than the procedural approach and there is no way to make it finer without repainting the atlases.

That asymmetry is worth being explicit about, because it points at the real cost of moving from code models to assets. Code models can vary along any axis you can express as a number. Asset models vary along exactly the axes somebody exported.

The failure mode to watch for

Position hashing has one weakness: it is uniform. Every tile is as likely to produce a yellow house as any other, so a town built on it has yellow houses evenly scattered through it.

Real towns are not uniform. Streets were built in batches, so houses cluster by age and colour, and the clusters are what make a town feel like it has a history.

Traluno does not do this yet, and the town is slightly poorer for it. The obvious fix — hash a coarser coordinate to pick a neighbourhood palette, then hash the fine coordinate within it — is about four lines and is on the list. It is a good example of a thing that looks finished, works, and is still leaving most of the available effect on the table.

  • models
  • procedural
  • town

◀ All posts