Traluno▶ Play Traluno

Building a Town That Does Not Repeat

How Traluno's procedural buildings vary: colours hashed from the cell each house stands on, jittered blocks, and why the glTF people cannot be tinted.

The demo town in morning sun seen from behind the table: cottages with red, brown, green and grey roofs and taller townhouses round a loop road with a yellow bus on it, a level crossing on the left, the church tower in the middle and the station with a steam train behind.

A model railway town is mostly the same building several times over, and Traluno's procedural buildings have that problem with fewer models than most. Real suburbs repeat too, so repetition is not automatically wrong. It becomes wrong the moment the eye notices, and a row of identical objects is exactly the kind of pattern it picks out first.

There are only a handful of house models. The demo layout a first visit opens on puts seventeen Cottages, nine Townhouses and six Corner Shops into its town, and they do not read as copies. 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 loads. 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. Each model takes the coordinates of the cell it stands on, runs them through a cheap integer hash, and reads its colours off the result. The same house on the same cell is the same house forever, across sessions and devices, and the save file carries nothing extra: a placed building is its type, its cell, its rotation and an id.

Move it one cell and it is usually a different house; about one move in eight lands on the same wall colour. 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 repaint it. Turning it does not repaint it. Rotation is not part of the hash, so a Cottage keeps its colours through every quarter turn and only moves its door to another face.

What varies, and what does not

On the houses, colour and nothing else. Shape variation is expensive to author and less effective than you would think: two houses of different silhouette side by side read as two different buildings, which is fine, but it does not fix a row of twelve. Trim, doors and the number of storeys are the same on every copy.

The Cottage is the fullest case. Its hash picks a wall colour from eight and a roof colour from five, and both come from the same hashed number, read against palettes of different lengths. The wall changes at every eighth of the range and the roof at every fifth, so together they make twelve distinct cottages rather than forty. The walls are soft, pale colours and the roofs dark ones: a spread within a family rather than free hue, which would produce a clown town.

Sixteen copies of the Cottage in two rows facing each other across a street on an otherwise bare table, their roofs in red, rose, brown, green and slate and their walls in pale cream, pink, grey and sand.
Sixteen Cottages, one model: each reads its wall and roof colours off its own cell.

The other houses vary less. The Townhouse picks one of four wall colours and keeps its flat roof. The Corner Shop picks one of three, and its awning comes with it as a pair. The Farmhouse is the Cottage with its wall fixed to white, so only its roof changes from cell to cell. The paid Victorian Villa and Apartment Block are not hashed at all.

Scenery uses the same trick for more than colour. A Leafy Tree or a Pine Tree takes its offset inside the cell, its size and its shade of green from the hash. The Crop Field picks one of three crops and how tall it has grown, and the Fair Booth picks its awning.

The per-cube jitter underneath

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

Every block in a built model has its brightness nudged a little from the model's base colour: up to nine per cent either way on a Cottage wall, a little more on trim and roofs. Ground, lawns and paving go further and pick each block's colour from a short list. The nudge is seeded from where the block sits inside the model, so it is stable across reloads, and every Cottage carries the same pattern of lighter and darker blocks. That is fine. The hashed palette tells two cottages apart; the jitter's job is to stop either of them being flat.

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 it makes the cube seams read as construction rather than as an artefact of the renderer.

A close view of one Cottage in afternoon sun: its pale pink wall built of blocks that are each a slightly different shade, white corner trim, a brown tiled roof, a door under a small hood and four-pane windows, with neighbouring cottages either side.
Close up, a Cottage wall is a field of slightly different blocks.

Turn it off and everything goes plastic. It is a single expression in the block builder, and 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. Two material languages covers where that line falls.

More interestingly, the glTF vehicles and figures get no per-instance colour at all. Their colours are baked into a shared texture atlas, so tinting the material would wash the whole model, hair, skin, clothes and all, rather than repaint the clothes. A tinted crowd is a crowd of monochrome people.

So variety there comes from a different mechanism. Each entry in the asset manifest can list several files, and each instance picks one. Eight files give eight different people, five give five different cars, 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. The pick is also random, the trap from the first section, which matters far less for things that move: the crowd is sent out afresh every time the layout loads anyway, and a car you placed can come back from a reload as a different car without anyone losing their bearings.

That asymmetry is worth being explicit about, because it points at the real cost of moving from code models to assets. The code pedestrian, which stands in whenever the files are missing, mixes five skin tones, six hair colours, nine shirts and five pairs of trousers, then adds a bag, a hat or a child's height by chance: more than ten thousand combinations. The glTF crowd is eight people. 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 cell 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 often built in batches by one builder, so neighbouring houses share an age, a brick and a paint scheme, 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, hashing a coarser coordinate to pick a neighbourhood palette and then the fine coordinate within it, is about four lines. It is a good example of a thing that looks finished, works, and is still leaving most of the available effect on the table.

Until then, the grouping is yours to do. The scenery ideas post goes through which free buildings make a town and where its level crossing should go, and the tour of buildings at night shows which of them light up after dark.

  • models
  • procedural
  • town

◀ All postsMore on scenery & buildings