Lesson 9.1Lesson 9.1 · Generative & Parametric Scripting
Randomness and Noise
Controlled chance as a design tool - from seeded random numbers to the smooth, organic variation of Perlin noise
Nothing in the built world is perfectly regular - and controlled randomness is how you put that life back into a generated design.
A hand-laid brick wall, a stand of trees, a stone floor: none of it is perfectly uniform, and that gentle irregularity is exactly what makes it feel real. A grid drawn by a computer, by contrast, is dead-flat regular - and often reads as sterile.
Randomness is the tool that puts life back in, but only if you keep it on a leash. Raw chance is chaos; a designer wants controlled variation - jitter within a range, a seed you can return to, and smooth noise that varies the way nature does. This lesson turns Python's random and noise tools into a design instrument you can actually steer.
Bound the chance: range, weights, seed, noise. Then let the computer explore.
The random module - and why a seed matters
Python ships with a random module in its standard library, so there is nothing to install. It is a generator of numbers that look random but are actually produced by a fixed mathematical recipe - which turns out to be a gift, not a flaw. The most useful three functions are random.random() (a float from 0 up to 1), random.uniform(a, b) (a float in a range you choose), and random.choice(seq) (one item picked from a list).
import random
width = random.uniform(0.8, 1.4) # a bay width, in metres
finish = random.choice(["oak", "ash", "teak"])
print(round(width, 2), finish)Run that and you get a different pair every time - which sounds like what you want, until you generate a facade you love and cannot get back. The fix is a seed. Calling random.seed(n) sets the generator's starting point, so the exact same sequence of 'random' numbers follows every time:
import random
random.seed(42)
print([round(random.random(), 2) for _ in range(3)])
# always: [0.64, 0.03, 0.28]Because the sequence is now reproducible, a seed becomes a compact name for a whole design. 'Try seed 42' regenerates one variant exactly; 'try seed 7' gives another. You can explore hundreds of options, note the seed of the one the client liked, and reproduce it on demand - randomness you can actually return to.
Same seed -> same sequence. A seed is a name for a whole random design.
Keeping chance on a leash - ranges, weights and jitter
The difference between randomness as a design tool and randomness as noise is restraint. You almost never want pure chaos; you want a controlled amount of variation around a sensible base. The pattern is to start from a regular value and add a small random jitter:
import random
random.seed(1)
spacing = 3000 # mm, a regular module
posts = [i * spacing + random.uniform(-120, 120) for i in range(6)]
print([round(p) for p in posts])Every post is near its ideal position but nudged by at most 120 mm - enough to break the mechanical rhythm, not enough to look broken. The range is the design decision.
Sometimes you want an uneven mix rather than an even one. random.choices() (note the 's') takes weights, so you can say 'mostly glass, occasionally a solid panel':
import random
random.seed(3)
panels = random.choices(["glass", "solid"], weights=[8, 2], k=10)
print(panels.count("glass"), panels.count("solid"))And random.shuffle() reorders a list in place - handy for scattering a fixed set of planters or artworks without repeating a pattern. The recurring idea across all of these: you are not asking for 'random', you are asking for 'varied, within these limits' - and the limits are where your judgement lives.
The shape of the randomness matters too, not just its range. random.uniform treats every value in the range as equally likely - a flat distribution. But many natural quantities cluster around a typical value and only occasionally stray far, which is a normal (bell-curve) distribution, and random.gauss(mean, sigma) gives you exactly that: most results land near the mean, extremes are rare. For a planting scheme where most trees are about three metres with a few notably taller, a Gaussian looks far more believable than a uniform spread. Choosing between a flat and a bell-shaped distribution is a genuine design decision - it is the difference between 'anything in this band, equally' and 'usually this, occasionally more'.
A small worked example ties it together. Suppose you are laying out a screen of vertical fins and want their depths to feel hand-made: a uniform jitter gives a restless, evenly-scattered look; a Gaussian jitter keeps most fins near a comfortable depth with a few standouts, which usually reads as more considered. Same range, different character - and swapping one function for the other is a one-line change you can try both ways and judge by eye.
You never want chaos - you want variation within limits. The range is the design.
Why raw randomness looks wrong - and noise looks right
There is a catch that surprises everyone the first time. If you jitter every element independently, the result often looks like static - restless and artificial - because there is no relationship between neighbours. A real hillside, a weathered wall or a windswept meadow does not jump randomly from point to point; nearby values are similar and change gradually. That gradual, correlated variation is called noise in the technical sense, and the famous version is Perlin noise (with its faster cousin, simplex noise), invented for computer graphics precisely to make things look natural.
The key difference: random.uniform has no memory - each value is independent. Perlin noise is a smooth function of position, so noise(1.0) and noise(1.05) are close, and the values flow rather than flicker. That is what makes noise the right tool for organic form: undulating roof heights, a stippled planting density, a gently varying facade depth. Independent randomness gives you gravel; noise gives you dunes.
The reason this matters so much in design is that our eyes are exquisitely tuned to the difference. Truly random arrangements actually look clumpy and artificial to us - stars scattered by pure chance form clusters and voids that feel wrong, which is why designers so often reach for either strict regularity or the gentle correlation of noise, and rarely for raw randomness in between. Understanding that continuum - regular at one end, correlated noise in the middle, independent chaos at the other - lets you place your design exactly where it should sit, rather than defaulting to whichever the first function you tried happened to give.
White noise = static. Perlin noise = neighbours related = organic.
Using Perlin noise in practice
Perlin noise is not in the standard library, but the small noise package provides it (install once with pip install noise). You feed it a coordinate and get back a smooth value, typically between -1 and 1, which you scale to whatever your design needs:
from noise import pnoise1
base = 3000 # mm, base roof height
for i in range(8):
x = i * 0.35 # step slowly to stay smooth
height = base + pnoise1(x) * 800
print(round(height))Step along x and the heights rise and fall in a continuous wave rather than jumping - a ridgeline, not a bar chart. Two knobs control the character: the step size or frequency (smaller steps = smoother, larger steps = busier) and the amplitude (the * 800, how much it varies). For 2D fields - a facade grid, a landscape - pnoise2(x, y) takes two coordinates and gives a smooth surface you can sample at every cell, which is how you drive an entire elevation of varied fin depths or a terrain mesh from a single continuous function.
There is a third dial worth knowing: octaves. Layering several noise functions at different frequencies and amplitudes - large slow waves plus small fast ripples - gives fractal noise (often called fBm, fractional Brownian motion), which is what makes computer landscapes and clouds look convincingly natural. The noise package exposes it directly through an octaves argument to pnoise1, so you rarely have to layer it by hand.
Be honest about the trade-off: noise is a look, not a meaning. It makes things appear organic, but it does not know about structure, drainage or daylight. Use it for expressive variation, then let real constraints - which the optimization lesson tackles - pull the result back toward something that also performs. Noise is the paintbrush; it is not the engineer.
Three dials: amplitude (how much), frequency (how busy), octaves (fractal detail).
Randomness across a team - and where not to use it
Two practical points turn randomness from a toy into something you can rely on in a studio. The first is collaboration. Because a seed makes a design reproducible, it also makes it shareable: 'facade, seed 42, jitter 120 mm' fully specifies a result, so a colleague running the same script gets the same wall. Without a seed, a generative design is unrepeatable, which means it cannot really be reviewed, checked or handed over - the seed is what makes it a proper deliverable rather than a one-off screenshot. Record the seed alongside the parameters, and treat the pair as the design's fingerprint.
The second is knowing where randomness does not belong. Anything that must be deterministic and defensible - structural sizing, egress widths, code-compliance calculations, cost take-offs - should not depend on chance. Randomness is for exploration and expression, not for decisions that need to be exactly right and justifiable. Even in expressive work, unbounded randomness is a trap: always constrain it to a range your judgement has approved, or you will spend more time rejecting garbage than designing.
You will also meet randomness inside your design tools directly. Grasshopper has Random and Jitter components, and Dynamo has random nodes, both of which take a seed input for exactly the reproducibility reasons above - so the mental model you build here transfers straight into Modules 6 and 7. Whether you write random.seed(42) in Python or wire a seed into a Grasshopper component, the discipline is identical: bound the chance, fix the seed, record it. That habit is the whole difference between randomness as a reliable design instrument and randomness as noise you cannot control.
Seed + params = a design fingerprint you can share. Never randomise safety-critical numbers.
random module
Python standard library for pseudo-random numbers
Ships with Python - no install. random(), uniform(a,b), choice(), choices(), shuffle() cover almost everything a designer needs.
random.seed()
Fixes the generator's starting point
Makes a random sequence reproducible. The single most important habit in generative work - it turns a lucky result into one you can get back.
Perlin / simplex noise
Smooth, correlated variation for organic form
Not in the standard library; the 'noise' package provides pnoise1/pnoise2. Neighbours are related, so results flow rather than flicker.
amplitude & frequency
The two dials that shape noise
Amplitude = how much it varies; frequency (step size) = how busy it is. Almost all noise tuning is these two knobs.
Workshop - a jittered, reproducible bay pattern
Generate a row of facade bays whose widths vary within a limit, prove the seed makes it reproducible, then find a variant you like and lock its seed. Everything here uses only the standard library.
Python 3 (random is built in). Optional: `pip install noise` for the Perlin step.
Goal: a controlled-random bay layout you can regenerate Inputs: Python 3, the random module Time: ~30 minutes
- 1Write a function
bays(n, base, jitter, seed)that callsrandom.seed(seed), then returns a list ofnwidths, eachbase + random.uniform(-jitter, jitter). Print the widths rounded. - 2Run it twice with the same seed and confirm the two outputs are identical - that is reproducibility. Then change only the seed and watch the whole pattern change.
- 3Sweep seeds 0 to 9 in a loop, printing the seed and the total width for each. Pick the variant whose look or total you prefer and note its seed - you have just 'chosen' a random design.
- 4Add variety: use
random.choices(["glass","solid"], weights=[7,3], k=n)to assign each bay a material, so most are glass and a few solid. - 5Bonus: install the
noisepackage and replace the uniform jitter withpnoise1(i * 0.3) * jitter, then compare - the noisy version should vary more smoothly along the row than the independent one.
You’ll walk away with
A short script that prints a reproducible row of varied bays with materials, plus a note recording the seed of the variant you chose and one sentence on how the Perlin version looked different from the uniform one.
Three altitudes on the same idea
Read the band that fits you — or all three.
Controlled randomness breaks the tyranny of the perfect grid. A repetitive facade, a monotonous colonnade or an over-regular masterplan can be given life with a seeded jitter that you can dial from 'barely perceptible' to 'clearly hand-made' and reproduce exactly for the client. Perlin noise drives undulating roofscapes, varied fin depths and naturalistic massing - expressive moves that would be tedious to place by hand, all regenerable from a single seed.
Randomness is how you make a scattered, curated look without it becoming a pattern. Think a wall of framed prints at slightly varied spacings, a terrazzo mix of aggregate sizes, a planting scheme, or a tile layout that avoids an obvious repeat. Weighted choices let you say 'mostly this finish, occasionally that accent', and a fixed seed means the arrangement you approved is the arrangement that gets specified.
Randomness and noise are your entry into generative design, and they are genuinely fun to play with. Seeds, ranges and Perlin noise are the exact building blocks behind the parametric facades and procedural landscapes in professional portfolios. They connect directly to the Computational Design and Generative AI courses in this Academy - and because the whole standard-library random module needs nothing installed, you can experiment tonight.
“Using randomness means giving up control of the design - you get whatever the computer spits out.”
Do it yourself
Reason about chance and control.
- 1What does
random.seed(42)do, and why is it the first line of most generative scripts? - 2You want post positions to vary by at most 100 mm around a 2400 mm spacing. Write the expression for one post at index i.
- 3What is the practical difference between
random.uniformand Perlin noise for varying a roofline? - 4How would you make a material choice come out 'mostly glass, sometimes solid'?
- 5Why can independent randomness look like static, and how does noise fix it?
The one line to carry out
Peer-reviewed journals & authoritative standards
- 01random - Generate pseudo-random numbers — Python documentation, 2026.
- 02Randomness — Wikipedia, 2026.
- 03Perlin noise — Wikipedia, 2026.
- 04Procedural generation — Wikipedia, 2026.
random module gives you uniform values, weighted choices and shuffles; a seed makes any of it perfectly reproducible, so a random design becomes one you can regenerate and specify. Independent randomness can look like static, so for organic variation you reach for Perlin or simplex noise, whose neighbouring values are related and therefore flow. Two dials - amplitude and step size - shape the result.Randomness varies a pattern; next we look at rules that _grow_ one. Recursion and L-systems let a tiny rule call itself to generate branching, fractal and plant-like complexity - simple instructions, elaborate form.
The author
Amogh N P
Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.
More about Amogh →