quality_score: 100 readability_score: 67
Randomness, Gaussian Distributions & Random Walk¶
Summary¶
Examines pseudo-random number generation, normal distributions, standard deviation, and random walk algorithms. Students will gain practical hands-on experience by building interactive sketches and visual experiments that demonstrate these concepts.
Concepts Covered¶
This chapter covers the following 16 concepts from the learning graph:
- Random Function Float
- Random Range Function
- Random Seed Setting
- Random Gaussian Normal
- Standard Deviation Spread
- Mean Center Value
- Random Walk Algorithm
- Levy Flight Algorithm
- Perlin Noise Concept
- Noise Function 1D
- Noise Function 2D
- Noise Function 3D
- Noise Scale Increment
- Noise Detail Octaves
- Noise Falloff Ratio
- Noise Seed Function
Prerequisites¶
This chapter builds on concepts from:
Welcome to Chapter 9!

Palette here! Perfectly predictable code is great, but sometimes you need a little bit of beautiful chaos. In this chapter, we're going to harness the power of unpredictability to make your art feel truly organic and alive. Time to color outside the loops!
Welcome to the wild side of programming! So far, we've mostly been telling the computer exactly what to do, pixel by pixel. If we draw a circle at coordinates (100, 100), it stays precisely at (100, 100). If we tell a shape to move across the screen, it moves with mathematical precision. But nature isn't like that. A falling leaf flutters unpredictably. A crowd of people doesn't walk in perfect grids. Even the texture of a rock has an element of unpredictability to it.
The Chaos and Order of Code¶
To create digital worlds that feel alive, organic, and natural, we need to introduce chaos. But we need a special kind of chaos—controlled chaos. In this chapter, we are going to explore different ways to generate "random" values, why some kinds of randomness look more natural than others, and how we can use these concepts to simulate everything from wandering bugs to rolling terrain.
The Basic Dice Roll: Uniform Randomness¶
Let's start with the most basic form of unpredictability: flipping a coin or rolling a die. When you roll a standard six-sided die, every number from 1 to 6 has an equal chance of appearing. This is known as a uniform distribution.
In p5.js, we have a built-in way to get this kind of randomness: the Random Function Float. If you call random() without any arguments, it returns a floating-point number between 0 and 1 (but not including exactly 1). It might give you 0.1432, 0.992, or 0.5.
If we want a number between 0 and a specific maximum, we can pass one argument to it. If we want a number between a specific minimum and maximum, we can pass two arguments. This is known as using the Random Range Function.
// Returns a random float between 0 and 50
let x = random(50);
// Returns a random float between 100 and 200
let y = random(100, 200);
Palette's Tip

Want a fast way to generate unpredictable visuals? Pass random(255) directly into your fill() color channels! It will flash a completely different RGB color every single frame, perfect for chaotic glitch effects.
While random() is incredibly useful, it has a quirk. Computers aren't actually capable of true randomness. They use complex mathematical formulas to generate sequences of numbers that appear random. These are called pseudo-random number generators (PRNGs).
Because these sequences are generated by a formula, if you know the starting point of the formula, you can predict the entire sequence of numbers it will produce! This starting point is called the "seed". By default, p5.js picks a different seed every time you run your sketch based on the current time. But sometimes, you want the same sequence of random numbers every time. Maybe you generated a beautiful random landscape and you want to show it to a friend.
To do this, we use Random Seed Setting. By calling randomSeed(seedValue), we lock the sequence.
function setup() {
createCanvas(400, 400);
// Setting the seed to 99 means the random sequence
// will be identical every time we run the program!
randomSeed(99);
for (let i = 0; i < 5; i++) {
console.log(random(10));
}
}
Darts vs. Coins: Normal Distribution¶
Let's go back to our coin flip metaphor. If you flip a coin 100 times, you expect roughly 50 heads and 50 tails. If you plot the results of random(0, 100), you'll get a flat block of results. Numbers near 10 appear just as often as numbers near 90.
But think about throwing darts at a dartboard. If you are aiming for the bullseye, where will the darts land? You probably won't hit the exact dead-center bullseye every time. But most of your darts will clump somewhere near the middle. You'll have fewer darts landing halfway to the edge, and very few darts hitting the outer edge (or the wall behind the board!).
Palette's Insight

Notice how a uniform distribution treats every pixel equally, but a Gaussian distribution creates a focal point? By shifting away from pure randomness, you introduce a 'center of gravity' to your generated shapes.
This "clumping" around a central value is called a Normal Distribution or a Gaussian Distribution (named after the mathematician Carl Friedrich Gauss). Nature loves normal distributions! If you measure the heights of adults in a city, most people are close to average height, while very short and very tall people are rare.
In p5.js, we can generate these kinds of numbers using Random Gaussian Normal. When you call randomGaussian(), it doesn't return a flat distribution. Instead, it returns numbers that are most likely to be near 0, but could theoretically be anything.
By itself, clustering around 0 might not be that helpful. We usually want to control where the "bullseye" is, and how wide the spread of the darts is. We can do this by passing two arguments to randomGaussian(mean, sd).
The first argument is the Mean Center Value. This is the bullseye—the average value that the random numbers will cluster around.
The second argument is the Standard Deviation Spread. This controls how tightly the numbers clump around the mean. A small standard deviation means you are a professional dart player—almost all your darts hit very close to the mean. A large standard deviation means you're throwing darts blindfolded—the darts will spread out widely.
function draw() {
// Mean (bullseye) is the center of the screen (width/2)
// Standard Deviation is 50 pixels
let x = randomGaussian(width/2, 50);
// Draw faint circles. They will cluster in the middle!
noStroke();
fill(0, 50);
circle(x, height/2, 10);
}
The Drunkard's Walk: Random Walks¶
Now that we have ways to generate random numbers, let's use them to make something move. One of the classic algorithms in computer science and physics is the Random Walk Algorithm.
Imagine a person who has had a bit too much to drink standing under a lamppost. They take a step. But because they are dizzy, the direction of that step is completely random. They might step north, south, east, or west. After that step, they pause, pick another completely random direction, and take another step.
Where do they end up over time?
If you trace their path, you'll get a squiggly, tangled line that slowly wanders away from the lamppost. This is a random walk. It's not just a silly metaphor; physicists use random walks to model how molecules bounce around in a gas or liquid (Brownian motion), and financial analysts use them to model stock prices.
Let's look at how we might program a basic Random Walk object.
class Walker {
constructor() {
this.x = width / 2;
this.y = height / 2;
}
display() {
stroke(0);
point(this.x, this.y);
}
step() {
// Pick a random number between 0 and 4
let choice = floor(random(4));
if (choice === 0) {
this.x++; // Move right
} else if (choice === 1) {
this.x--; // Move left
} else if (choice === 2) {
this.y++; // Move down
} else {
this.y--; // Move up
}
}
}
Every frame, the walker picks one of four directions and moves a single pixel. Over time, it leaves a trail.
Diagram: Random Walk Simulation¶
Run Random Walk Simulation Fullscreen
MicroSim: Random Walk Simulation
MicroSim: Random Walk Simulation
**Goal:** Create a visual comparison of different types of Random Walks. **Features:** - A canvas showing the paths of three different colored "Walkers". - Walker 1 (Red): Standard Uniform Random Walk (moves 1 pixel up/down/left/right randomly). - Walker 2 (Blue): Gaussian Random Walk (uses `randomGaussian()` for step size, tending to stay closer but occasionally taking larger steps). - Walker 3 (Green): A Walker that has a slight bias to move toward the mouse cursor. - A "Reset" button to clear the canvas and put all walkers back in the center. - A slider to control the simulation speed (how many steps they take per frame). **Interactivity:** - Moving the mouse should influence the Green walker to slowly drift toward the cursor's location, demonstrating a "biased" random walk.Taking Flight: The Levy Flight¶
While a standard random walk is interesting, it tends to stay very close to its starting point. It spends a lot of time backtracking over its own path.
If you observe animals foraging for food in the wild—like an albatross searching the ocean, or a shark hunting, or even a spider exploring—they don't use a standard random walk. Instead, they use a strategy called a Levy Flight Algorithm.
A Levy Flight is a random walk where most of the steps are very short, but occasionally, the walker takes a massive, long-distance jump to a completely new area. It clusters around one spot, searching it thoroughly, and then zoom! it flies off to a new distant spot to search there.
We can simulate a Levy flight by picking a random step size, but making small steps highly likely and huge steps rare.
step() {
let stepX = random(-1, 1);
let stepY = random(-1, 1);
// 1% chance to take a massive leap!
let r = random(1);
if (r < 0.01) {
stepX *= random(20, 100);
stepY *= random(20, 100);
} else {
stepX *= 2;
stepY *= 2;
}
this.x += stepX;
this.y += stepY;
}
TV Static vs. Rolling Hills: Perlin Noise¶
We've covered uniform randomness and Gaussian randomness. But there's a big problem with using random() for graphics: it's too random.
Imagine you want to draw a mountain range. You might try drawing a line across the screen and using random() to set the Y coordinate (the height) at every X pixel. What does it look like?
It looks like TV static. The height jumps wildly from pixel to pixel. There's no connection between the height at pixel 10 and the height at pixel 11.
But real mountains aren't like that. A mountain is a continuous slope. If the height at pixel 10 is 500, the height at pixel 11 should be very close to 500, like 498 or 502. It shouldn't suddenly jump to 100. We want "smooth" randomness. We want hills, valleys, and curves.
This is where the Perlin Noise Concept comes in. Invented by Ken Perlin in the 1980s (for the movie Tron!), Perlin noise is a special algorithm designed to produce pseudo-random sequences that are smoothly interpolated. It generates "organic" randomness.
When you ask for Perlin noise, you don't just ask for a random number. You ask for the noise value at a specific coordinate.
Let's start with a Noise Function 1D. Imagine a perfectly smooth, wavy line stretching out infinitely. In p5.js, we use noise(x). You pass in an X coordinate, and it returns the height of the wavy line at that exact spot. The return value is always a float between 0.0 and 1.0.
Because the line is smooth, noise(10.0) and noise(10.1) will return values that are very close to each other.
let t = 0;
function draw() {
// Get a noise value based on time 't'
let n = noise(t);
// Map the noise value (which is 0 to 1) to the screen width
let x = map(n, 0, 1, 0, width);
circle(x, height/2, 50);
// Move forward in time by a tiny amount
t += 0.01;
}
Two and Three Dimensions of Noise¶
Perlin noise isn't limited to a one-dimensional timeline. What if we want to generate a 2D map, like clouds in the sky or terrain on a map?
We can use the Noise Function 2D. Instead of a wavy line, imagine an infinitely large, wavy sheet (like a rumpled blanket). If you pass two coordinates into the noise function—noise(x, y)—it returns the height of the blanket at that specific grid point.
By looping over every pixel on the screen and mapping the noise(x, y) value to a grayscale color, we can draw beautiful, cloudy textures.
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
// Generate noise value for this pixel
let n = noise(x * 0.01, y * 0.01);
// Map 0-1 to grayscale 0-255
let c = map(n, 0, 1, 0, 255);
stroke(c);
point(x, y);
}
}
We can even go further! A Noise Function 3D takes three coordinates: noise(x, y, z). How do you visualize 3D noise? One common way is to use the first two coordinates for space (X and Y on the screen) and the third coordinate for time. As the Z coordinate slowly increases, the 2D cloud pattern smoothly morphs and animated, looking exactly like flowing lava or shifting storm clouds!
Sculpting the Noise¶
Perlin noise is powerful, but we need to know how to tame it. Let's look at the parameters that control its shape.
First, there is the Noise Scale Increment. Notice in the 2D noise example above, we didn't just write noise(x, y). We wrote noise(x * 0.01, y * 0.01). Why multiply by 0.01?
Remember that noise changes smoothly. If we jump from noise(1) to noise(2), we are taking a massive leap across the noise landscape. The values will look completely unrelated, and we'll be back to TV static. To see the smooth hills, we have to take tiny baby steps. We need to look at noise(0.01), then noise(0.02), then noise(0.03).
The amount we increase our input variable by is the increment. A small increment "zooms in" on the noise, making huge, smooth, rolling hills. A large increment "zooms out," making the hills look jagged and choppy.
Next, we can control the detail of the noise. The noise function is actually built by layering multiple layers of wavy lines on top of each other. Each layer is called an octave. The first octave is the big, main rolling hills. The second octave adds smaller bumps on top of those hills. The third octave adds even tinier rocky details on top of the bumps.
We can control how many layers are used with Noise Detail Octaves. By default, p5.js uses 4 octaves. If we change it using noiseDetail(lod), where lod (level of detail) is the number of octaves, we can change the texture. noiseDetail(1) will look incredibly smooth and blurred. noiseDetail(8) will look very textured, highly detailed, and granite-like.
When layering these octaves, the smaller detail layers don't have as much influence as the big main layers. The rate at which the smaller layers lose influence is called the Noise Falloff Ratio. By default, each smaller octave has half the amplitude (50% falloff) of the one before it. You can adjust this by passing a second argument to noiseDetail(octaves, falloff). A higher falloff means the tiny details are stronger, making the result look sharper and rougher.
Diagram: Perlin Noise Terrain Generation¶
Run Perlin Noise Terrain Generation Fullscreen
MicroSim: Perlin Noise Terrain Generation
MicroSim: Perlin Noise Terrain Generation
**Goal:** Create an interactive 2D map generator using Perlin Noise to demonstrate scale, octaves, and falloff. **Features:** - A canvas displaying a top-down 2D map generated with `noise(x, y)`. - The noise values should be mapped to colors to represent terrain: deep water (blue), shallow water (light blue), sand (yellow), grass (green), forest (dark green), and snow (white). - Sliders for: - **Noise Scale:** Controls the multiplier (increment) applied to the X and Y coordinates before passing them to the noise function. Ranging from very smooth "zoomed in" terrain to very noisy "zoomed out" terrain. - **Octaves:** Controls the `lod` parameter of `noiseDetail()`, from 1 to 8. - **Falloff:** Controls the `falloff` parameter of `noiseDetail()`, from 0.0 to 1.0. - A "Generate New Seed" button that changes the noise seed. **Interactivity:** - As the user drags the sliders, the terrain map dynamically recalculates and redraws in real-time.Finally, just like the random() function, Perlin noise sequences are generated mathematically. Every time you restart your sketch, you get a new landscape. If you want to explore the exact same landscape again, you need to use a Noise Seed Function. By calling noiseSeed(seedValue), you lock the Perlin noise generator to a specific mathematical starting point, guaranteeing the exact same rolling hills every time.
Palette's Warning

Watch out for 2D movement sliding diagonally! If you pass the exact same time variable t into noise() for both the X and Y coordinates, they will output the identical sequence. Always offset the second axis (like noise(t + 1000)) to get independent, natural wandering.
Conclusion¶
We've moved beyond the rigid, mechanical world of perfect grids and predictable loops. By mastering randomness, Gaussian distributions, and Perlin noise, you now have the tools to breathe life into your code. You can make particles that jitter like gas molecules, creatures that wander with purpose, and sprawling, organic landscapes that look like they were carved by nature.
These aren't just parlor tricks; these are the exact same algorithms used by professional game developers to generate infinite worlds (like Minecraft), by VFX artists to create realistic smoke and water, and by scientists to simulate complex physical systems.
In the next chapter, we will start using these organic movements and shapes to build even more complex systems of interacting objects!
Chapter Complete!

Incredible work! You just mastered uniform randomness, Gaussian distributions, and organic Perlin noise. You can now inject lifelike, natural chaos into your digital simulations!