Skip to content

Perlin Noise Landscapes & Vector Flow Fields

Summary

Introduces 1D/2D/3D Perlin noise, octaves, detail settings, organic terrain generation, and vector flow fields. 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 18 concepts from the learning graph:

  1. Smooth Noise Landscape
  2. Noise Animated Terrain
  3. Noise Flow Field Concept
  4. Vector Field Direction
  5. Organic Curve Generation
  6. Noise Texture Map
  7. Wobbly Circle Generator
  8. Perlin Noise vs Random
  9. Simplex Noise Derivative
  10. Worley Cell Noise
  11. Monte Carlo Selection
  12. Weighted Random Choice
  13. Shuffle Array Order
  14. Stochastic Tree Growth
  15. Brownian Motion Model
  16. Cloud Texture Generator
  17. Marble Texture Generator
  18. Noise Displacement Map

Prerequisites

This chapter builds on concepts from:


Welcome to Chapter 10!

Palette waving welcome

Welcome, creators! If standard randomness looks too chaotic and noisy, wait until you discover the smooth, rolling hills of Perlin noise. This chapter will teach you how to generate stunning, natural textures and mesmerizing flow fields. Let's blend some code and build worlds!

Welcome to the magic of organic randomness! In the previous chapter, we looked at pure randomness—like flipping a coin, rolling a die, or wandering aimlessly in a random walk. But if you look around at the real world, things are rarely completely chaotic or disconnected. Mountains don't look like television static; they have sweeping curves, ridgelines, and valleys that connect logically from one point to the next. Clouds have soft, puffy shapes that gently transition into the clear blue sky. Wood has flowing grain patterns that swirl smoothly around knots. Nature is undoubtedly random, but it is a smooth, connected kind of random. It has structure, flow, and memory.

This chapter is all about capturing that organic, natural feel in our code. We will explore how to tame randomness, smooth it out, bend it to our will, and use it to simulate the beautiful complexity of the physical world. By the end of this chapter, you won't just be generating static on a screen; you will be growing forests, shaping mountains, and painting with invisible digital wind.

The Problem with Pure Randomness

To understand why we need new tools, we have to understand the limitations of the tools we already have. When we use a standard random() function in programming, each value it gives us is completely, 100% independent of the value that came before it. If random() gives us a 0.9, the next call is just as likely to give us a 0.1 as a 0.8.

If we were to draw a line graph of these pure random values, the line would jump wildly and violently up and down. This is called "white noise." It is chaotic, unpredictable, and harsh. This is perfect for simulating a roll of the dice in a game of Monopoly, or randomly shuffling a deck of cards.

But imagine you are trying to write a program that draws a natural horizon line—the silhouette of a distant mountain range. If you use pure randomness to pick the height of each pixel along the screen, you won't get a mountain. You will get a jagged, fuzzy mess that looks like a barcode or static on a broken television. The mountain lacks continuity. In the real world, if a mountain is 500 meters high at one spot, it is physically impossible for it to be 10 meters high just one step to the right. The heights of adjacent points in space are correlated; they are related to each other.

This was exactly the problem facing computer graphics pioneers in the early 1980s.

The Birth of Perlin Noise

In the early 1980s, a computer scientist named Ken Perlin was working on the groundbreaking sci-fi movie Tron. At the time, computer-generated imagery (CGI) was in its infancy. Everything rendered by computers looked perfectly flat, mathematically smooth, and incredibly artificial. Things looked like plastic.

Perlin wanted to make surfaces look more realistic. He wanted to give objects the appearance of dirt, rust, marble, and skin. But when he tried using pure random numbers to generate textures, it just looked like white noise. It didn't look like dirt; it looked like a broken computer.

To solve this, he invented a brand new mathematical algorithm. He created a type of "gradient noise" that produced a sequence of random numbers that smoothly transitioned from one to the next. He called it Perlin noise. The algorithm was so revolutionary, so fundamentally important to the future of computer graphics, that Ken Perlin was later awarded an Academy Award for Technical Achievement. Today, Perlin noise is used in almost every video game, animated movie, and generative art piece to create natural-looking textures and movement.

Perlin Noise vs Random

The crucial difference when comparing Perlin Noise vs Random is the concept of continuity.

Think of random() as a grasshopper trapped in a box, instantly teleporting from one random spot to another. You have no idea where it will appear next.

Think of Perlin noise (accessed via the noise() function in p5.js) as an ant walking across a bumpy terrain. You might not know exactly where the ant will be in ten minutes, but you know for an absolute fact that one second from now, the ant will be very close to where it is right now. It can't teleport. It must walk a continuous path.

When you pass a value (like time or a coordinate) into the noise(t) function, it returns a value between 0 and 1. If you pass in noise(1.0) and it returns 0.6, and then you pass in noise(1.01), the return value is mathematically guaranteed to be very close to 0.6, perhaps 0.61 or 0.59.

Continuity is Key

Palette thinking Think about it like this: random() has no memory of the past, like throwing darts blindly. But noise() remembers its neighbors! It builds a continuous world because it's mapping a connected landscape.

One-Dimensional Noise: The Smooth Noise Landscape

The simplest way to use Perlin noise is in one dimension (1D). We typically pass in a variable representing time (often called t or xOffset) that slowly increases each frame.

If we map the output of the noise() function to the y-coordinate of a point, and slowly pan our x-coordinate across the screen, we generate a Smooth Noise Landscape. Instead of harsh, jagged spikes, the line swoops gracefully up and down, creating gentle hills and valleys.

The secret to controlling this landscape is the "step size"—how much we increase our time variable t between each reading. - If we increase t by a large amount (e.g., t += 1.0), we are taking massive leaps across the noise space, and the output will look just like chaotic random(). - If we increase t by a very small amount (e.g., t += 0.01), we are taking tiny, cautious steps, and the landscape will be incredibly smooth and rolling.

By using this 1D noise, you can procedurally generate the ground for a 2D side-scrolling video game, ensuring the player always has a smoothly varying path to run across, without ever having to draw the level yourself by hand.

Frozen in Time?

Palette warning Watch out for stagnant noise! If you pass the exact same t value into noise() every single frame, your landscape will be completely frozen. Always remember to add a tiny amount (like t += 0.01) inside your draw() loop to make the landscape scroll!

Two-Dimensional Noise: Textures and Maps

Perlin noise becomes truly magical when we expand it into two dimensions. Instead of just passing in a single value t, we pass in two coordinates: x and y.

Think of 2D noise as a vast, infinite, invisible landscape of rolling hills and valleys. When we ask for noise(x, y), we are asking the computer, "What is the altitude of the terrain at this specific GPS coordinate?"

We can use this to create a Noise Texture Map. Imagine a grid of pixels on your screen. For every single pixel, we calculate its x and y position, feed those into the noise(x, y) function, and get back a value between 0 and 1. We then translate that value into a grayscale color (0 = black, 1 = white, 0.5 = gray).

Because the noise is continuous across both the X and Y axes, the resulting image doesn't look like static. It looks like beautiful, blurry, organic clouds. The bright white patches smoothly fade into gray, which fade into deep black pools.

Layering Detail: The Cloud Texture Generator

A single pass of Perlin noise looks a bit like an out-of-focus photograph. It's too smooth to look like a realistic natural surface. To make it realistic, we use a technique called "Fractional Brownian Motion" (fBm), which is the engine behind a realistic Cloud Texture Generator.

The idea is to add multiple layers of noise on top of each other. These layers are called "octaves," borrowing a term from music. - Octave 1 (The Fundamental): We start with large, smooth noise. This defines the overall shape—the big continents of our map, or the main body of a cloud. - Octave 2: We add a second layer of noise, but we double the "frequency" (we zoom out, making the bumps closer together) and halve the "amplitude" (we make the bumps less tall). This adds medium-sized details, like peninsulas or smaller cloud puffs. - Octave 3: We double frequency and halve amplitude again. This adds tiny, sharp details, like rugged coastlines or wispy cloud edges.

By adding 4, 6, or 8 octaves together, we create a texture that has massive, sweeping forms, but is heavily textured with intricate, sharp details when you zoom in. This is exactly how massive open-world video games generate endless, detailed terrain on the fly.

Distorting Math: The Marble Texture Generator

We don't have to just map noise directly to black and white. We can use noise to distort other mathematical functions.

Consider a simple sine wave mapped across the screen. It creates perfectly straight, repeating black and white stripes. It's very rigid and mathematical.

But what if we take the x coordinate of our pixel, add a value derived from Perlin noise, and then feed that warped coordinate into the sine wave function? The perfectly straight stripes will be pushed and pulled out of alignment by the underlying noise landscape.

This technique creates a Marble Texture Generator. The straight lines become wavy, swirling bands that look exactly like the natural veins running through a polished slab of marble or the rings inside a cross-section of wood. We are using the chaotic nature of noise to add organic imperfection to the rigid perfection of geometry.

3D Noise and the Noise Displacement Map

Perlin noise can exist in 3D space, too (noise(x, y, z)).

If we have a flat 2D grid of triangles in a 3D environment (like p5.js in WEBGL mode), we can use a 2D noise map not to color the pixels, but to physically lift the vertices of the grid upwards along the Z-axis. This is called a Noise Displacement Map. The bright spots push the geometry high to form mountains, and the dark spots leave the geometry low to form oceans.

If we take our 2D terrain and slowly change the y coordinate that we use to sample the noise, the entire landscape will appear to scroll toward the camera. We have created a Noise Animated Terrain! The mountains and valleys will ripple and flow smoothly, creating the illusion that we are flying an airplane endlessly over a procedurally generated planet.

Alternative Algorithms: Simplex and Cellular Noise

Ken Perlin wasn't finished. In 2001, he recognized that his original algorithm had some limitations. When scaling up to higher dimensions, classic Perlin noise became computationally expensive. Furthermore, because it was calculated on a square grid, it sometimes produced subtle visual artifacts—if you looked closely, you could see faint vertical and horizontal alignments in the noise.

To solve this, he developed the Simplex Noise Derivative. Instead of calculating values on a square grid, Simplex noise uses a grid of triangles (or tetrahedra in 3D space). This brilliant shift drastically reduced the computational cost and eliminated the grid-like artifacts, resulting in a cleaner, faster, and more isotropic (uniform in all directions) noise. Simplex noise has largely become the modern industry standard for high-performance graphics.

Another completely different approach is Worley Cell Noise (also known as Voronoi noise), invented by Steven Worley in 1996.

While Perlin noise creates continuous hills and valleys (like clouds), Worley noise creates distinct, cellular regions. Think of Worley noise as bubbles in a pot of boiling water.

The algorithm works like this: 1. It randomly scatters a set of "feature points" across the canvas. 2. For every pixel on the screen, it calculates the distance to the closest feature point. 3. It uses that shortest distance to determine the pixel's color (e.g., closer pixels are darker, further pixels are lighter).

The result is a stunning pattern that looks like microscopic biological cells, dry cracked earth, the surface of a sponge, or the scales on a reptile.

The Magic of Vector Field Direction

Now we arrive at one of the most mesmerizing and popular techniques in all of generative art: the Noise Flow Field Concept.

Imagine a large, open field. You are holding a handful of dandelion seeds. You release them into the air. Do they fall straight down? Do they move randomly, zigzagging like a random walk? No. They are caught by the wind. They flow together, tracing out the invisible currents of the air.

We can simulate these invisible currents using Perlin noise.

Instead of using the output of noise(x, y) to set a pixel's color or a mountain's height, what if we use it to set an angle? We map the noise value (which is between 0 and 1) to an angle between 0 and 360 degrees (or \(0\) to \(2\pi\) radians).

By calculating this for every point on a grid, we create a Vector Field Direction map. It is essentially a mathematical weather map. Every location on the screen has an invisible arrow pointing in a specific direction. Because Perlin noise is smooth and continuous, adjacent arrows will point in very similar directions. The arrows don't point chaotically; they form sweeping currents, swirling eddies, and smooth rivers of force.

When we drop thousands of tiny digital "particles" onto our canvas, each particle looks at the invisible arrow directly beneath it and takes a tiny step in that direction. As the particles move, they leave a trail behind them.

This results in Organic Curve Generation. The particles group together, flowing along the noise ridges and valleys, drawing thick, sweeping lines that look remarkably like hair, wood grain, muscle fibers, or long-exposure photographs of river currents.

Diagram: Flow Field Visualizer

Run Flow Field Visualizer Fullscreen

MicroSim: Flow Field VisualizerMicroSim: Flow Field Visualizer **Goal:** Visualize how a grid of angles generated by 2D Perlin noise creates a flow field, and observe how particles follow those invisible currents to draw organic lines. **Type:** p5.js Interactive Simulation **Features:** 1. **The Vector Grid Layer:** A background grid of short, semi-transparent lines or arrows. Each arrow's rotation is determined by sampling `noise(x, y)` at that grid location. 2. **Noise Scale Slider:** A control to adjust the "zoom level" of the noise. Lower values create broad, sweeping, gentle winds. Higher values create tight, chaotic, turbulent mini-whirlpools. 3. **Z-Offset Animation (Time):** A slider that slowly increments a 3rd dimension (`z`) in the noise function, causing the underlying vector field arrows to slowly rotate and shift over time, simulating shifting wind patterns. 4. **Particle Emitters:** Hundreds of bright particles that move across the screen. At every frame, each particle calculates its position, looks up the vector angle at that spot, and updates its velocity to move in that direction. 5. **Trail Toggle:** A checkbox that toggles whether the canvas clears every frame. When trails are left on, the particles paint the screen, highlighting the emergent **Organic Curve Generation**. **Interaction:** The user can manipulate the noise landscape in real-time, watching the particle streams break apart, merge, and swirl in response to the changing mathematical winds.

Morphing Shapes: The Wobbly Circle Generator

The concept of continuous 1D noise can be applied to geometry to create organic, breathing shapes.

Imagine you want to draw a circle. Normally, you use trigonometry (sine and cosine) to calculate the \(x\) and \(y\) coordinates of points along a fixed radius. This draws a perfectly rigid, mathematical circle.

But what if we make the radius variable? As we calculate the points around the circumference (from 0 to 360 degrees), we map the angle to a 1D Perlin noise lookup. We add or subtract a little bit of radius based on the noise value.

Because the noise is continuous, the circle doesn't become jagged; it becomes a Wobbly Circle Generator. It looks like an amoeba, an ink splat, or a squishy balloon.

If we take this a step further and add a time variable to our noise lookup (effectively sliding our circular sampling window through a 2D noise landscape), the wobbly circle will appear to breathe, morph, and squirm on the screen like a living microscopic organism.

Advanced Stochastic Methods: Controlling Chaos

"Stochastic" is a complex-sounding word for a simple concept: systems that are randomly determined, but follow statistical probabilities. In generative art, we rarely want pure, unadulterated randomness. We want to direct the randomness. We want to stack the deck in our favor.

Biasing the Dice: Weighted Random Choice

Imagine you are programming a role-playing game (RPG). When a player defeats a monster, they get "loot." You want a 70% chance they get a simple iron sword, a 25% chance they get a steel sword, and a 5% chance they get the legendary flaming sword.

A standard random() function simply picks a number between 0 and 1, giving all outcomes equal probability. To achieve our RPG loot system, we use a Weighted Random Choice.

We assign a "weight" to every item (Iron: 70, Steel: 25, Legendary: 5). We sum up all the weights (Total: 100). We pick a random number between 0 and the Total. We then iterate through our items, subtracting their weight from our random number until we hit zero or drop below it. Whichever item causes us to hit zero is our selection!

This allows us to inject extreme rarity and controlled scarcity into our generated worlds.

Palette's Tip

Palette tip Here's a secret for easy balancing: don't use percentages for everything! Just give common items huge whole-number weights (like 1000 for oak trees) and rare items tiny weights (like 2 for magic mushrooms). Sum them up, and your procedural world will instantly feel realistic.

Shaping Probability: Monte Carlo Selection

Sometimes, we don't have distinct items to choose from; we have a continuous range of numbers, but we want to skew the results. For example, you want to pick a random size for rocks on the ground. You want lots of small pebbles, a few medium rocks, and very rarely, a massive boulder.

We can achieve this using a broad class of algorithms called Monte Carlo Selection (named after the famous casino in Monaco).

A common Monte Carlo technique is the "accept-reject" method: 1. Pick a random number \(R_1\) between 0 and 1 (this represents the rock size). 2. We want smaller rocks to be more likely, so the probability of keeping \(R_1\) should be higher when \(R_1\) is small. 3. Pick a second random number \(R_2\) between 0 and 1. 4. If \(R_2\) is less than a calculated probability curve (e.g., \(1 - R_1\)), we "accept" \(R_1\) and use it! 5. If it fails the test, we "reject" it, throw it away, and start over at step 1.

Because a large rock size (say, 0.9) has a very low probability curve (1 - 0.9 = 0.1), it is very hard for the second random number to be smaller than 0.1. Thus, large rocks are frequently rejected, and the final output organically clusters heavily toward the smaller pebble sizes.

Ensuring Fairness: Shuffle Array Order

When dealing with a deck of cards or a playlist of songs, picking random items with replacement is a bad idea (you might pick the Ace of Spades twice). We need to randomize the order of an existing list without destroying or duplicating any elements.

The gold standard for this is the Fisher-Yates shuffle algorithm. It allows us to seamlessly Shuffle Array Order. The algorithm is elegant and efficient: It loops backward through the array. For each position, it picks a random index from the unshuffled portion of the array and swaps the elements. In just one pass through the data, it guarantees a perfectly fair, mathematically unbiased randomized order every single time.

Simulating Physics: The Brownian Motion Model

If you observe pollen grains suspended in water under a powerful microscope, you'll see them jiggling around erratically. This isn't because the pollen is alive; it's because millions of invisible, microscopic water molecules are constantly slamming into the pollen grain from all directions.

This erratic, jittery path is called the Brownian Motion Model (named after botanist Robert Brown).

In our code, we can simulate this easily using a random walk. Every frame, we take an object's current \(x\) and \(y\) position, and we add a tiny, random positive or negative value to both. x += random(-1, 1); y += random(-1, 1);

While the movement of a single particle seems like chaotic noise, when you apply the Brownian Motion Model to millions of particles, fascinating emergent behaviors appear. The particles diffuse outward from the center, perfectly simulating how a drop of ink slowly spreads and mixes when dropped into a glass of water.

Growing Complexity: Stochastic Tree Growth

Finally, we can combine our knowledge of structure (algorithms) and chaos (randomness) to simulate biological growth.

Consider a tree. A tree grows through recursion: a trunk splits into two branches; those branches split into two twigs; those twigs grow leaves. We can easily write a recursive function (like an L-System) to draw this. However, a purely mathematical recursive tree looks stiff, symmetrical, and artificial. It looks like a crystalline antenna, not a living oak.

To make it live, we implement Stochastic Tree Growth. We take our rigid recursive algorithm and inject tiny doses of controlled randomness (stochasticity) at every step. - When a branch splits, the angle isn't always exactly 30 degrees. We add a random variance so it might be 25 degrees or 35 degrees. - The length of the new branches isn't always exactly 70% of the parent. We use a Gaussian distribution (bell curve) to make some branches slightly stubby and others reaching and long. - We might even use probability to decide if a branch splits into three instead of two, or if it dies off early.

By allowing randomness to influence the parameters of a structured system, the resulting forms are endlessly unique. They look wind-blown, sun-seeking, and organic.

Diagram: Stochastic L-System Tree

Run Stochastic L-System Tree Fullscreen

MicroSim: Stochastic L-System TreeMicroSim: Stochastic L-System Tree **Goal:** Understand how small amounts of controlled randomness applied to recursive branching logic create highly lifelike, organic structures. **Type:** p5.js Interactive Generation **Features:** 1. **Base Recursion:** A function that draws a line, translates the drawing context to the end of the line, rotates by a specific angle, scales down, and calls itself twice to draw the left and right branches. 2. **Angle Variance Slider:** Controls the maximum random deviation applied to the split angle. At 0, the tree is a rigid, perfectly symmetrical fractal. As the value increases, branches twist and bend irregularly. 3. **Length Variance Slider:** Controls the randomness applied to branch lengths, making some sides of the tree reach further than others, simulating an uneven search for sunlight. 4. **Generate Seed Button:** Resets the random seed, instantly growing a brand new, unique tree using the current stochastic parameters. **Interaction:** By playing with the variance sliders, the user actively witnesses the transition from cold mathematical geometry into warm, **Stochastic Tree Growth**.

Conclusion: Taming the Chaos

Pure randomness is chaotic and disconnected, like a TV tuned to a dead channel. But the natural world is full of connected randomness: the smooth contours of a Smooth Noise Landscape, the swirling currents of a Vector Field Direction, and the beautiful, unpredictable branching of Stochastic Tree Growth.

By mastering tools like Perlin noise, Monte Carlo selection, and weighted probabilities, you elevate your code. You transition from simply drawing pixels to simulating the fundamental forces of nature. You learn to tame the chaos, giving it memory, structure, and flow.

In the next chapters, we will take these organic shapes and generated worlds and begin wrapping them into reusable classes and complex objects!

Chapter Complete!

Palette celebrating Incredible work! You just mastered generating Perlin noise landscapes, directing vector flow fields, and shaping probability with Monte Carlo selection! Your generative worlds are about to look stunning.

See Annotated References