In This Tutorial
📜 A Brief History
Ken Perlin & the Road to Hollywood
In the early 1980s, a young computer scientist named Ken Perlin was working as a programmer on special effects for the landmark film TRON (1982). The goal was to generate synthetic textures that looked organic — like fire, water, and alien terrain — using a computer.
The problem? Standard random number generators created textures that looked jagged and artificial. Nature doesn't work that way. Real smoke, real water, and real clouds all have structure — they change gradually, not abruptly. Perlin needed a better kind of randomness.
His refined technique became widely recognized when it powered the famous Genesis Planet effect in Star Trek II: The Wrath of Khan (1982) — one of the first fully computer-generated sequences in a major motion picture.
In 1997, Ken Perlin received a Technical Achievement Award from the Academy of Motion Picture Arts and Sciences (yes, the Oscar® people!) for his invention. Perlin noise is now used in virtually every area of computer graphics: game development, film VFX, procedural world generation, and — as you'll see — making simulated bugs crawl smoothly across a canvas.
❓ The Problem with Pure Randomness
When you call Math.random() in JavaScript (or p5.js's
random()), you get a number that has no relationship to
the previous call. Each result is completely independent.
random(): 0.87
random(): 0.12
random(): 0.95
random(): 0.03
random(): 0.76
random(): 0.41
Each call is completely unrelated to the last — wild, unpredictable jumps!
noise(0.0): 0.50
noise(0.1): 0.54
noise(0.2): 0.60
noise(0.3): 0.63
noise(0.4): 0.61
noise(0.5): 0.57
Values change gradually — smooth, continuous transitions!
Imagine you're animating a character walking. With pure random direction changes, the character would instantly teleport in a random direction every frame — clearly unrealistic. With Perlin noise, the direction changes are gradual and smooth, just like a real creature meandering around.
🎵 Analogy: Static vs. Music
Think of pure random numbers as static on a radio — every moment is completely disconnected from the next. Perlin noise is like music — still varied and surprising, but with flow and connection between notes. Perlin noise is "structured randomness": unpredictable in the long run, but smooth in the short run.
🔍 What Is Perlin Noise?
Perlin noise is a type of gradient noise — a mathematical function that generates smoothly-varying pseudo-random values. The critical property is that inputs that are close together produce outputs that are close together.
noise(1.0) returns 0.6, then
noise(1.01) will return something very close to 0.6.
noise(1.1) might differ a bit more, and
noise(5.0) could be quite different — but changes are always
gradual. There are no sudden jumps.
How It Works (Conceptually)
Here's the idea behind Perlin noise, simplified for intuition:
Grid of Gradients
Imagine an invisible grid in space. At each grid intersection, assign a random direction (a gradient vector) — like a tiny arrow pointing somewhere random.
Smooth Blending
For any point in space, blend the influence of the nearest grid arrows using a smooth interpolation curve (not a sharp step). This is what creates the smooth transitions.
A Smooth Value
The result is a single number between 0 and 1. Neighboring positions get similar values; distant positions are unrelated.
📐 The "Time" Dimension
Perlin noise can be evaluated in 1D, 2D, 3D, or even higher dimensions. In p5.js,
noise(t) samples a 1D "noise landscape" at position
t.
By slowly increasing t each frame (e.g.,
t += 0.01), you "walk" along that landscape and get
smoothly changing values. The step size controls how fast things change:
• Small step (t += 0.001)
→ very slow, gentle changes — lazy wandering
• Large step (t += 0.1)
→ faster, more noticeable changes — approaching random
🎮 Demo 1: Random Walk vs. Perlin Movement
Watch two dots move around the canvas. The red dot uses pure random direction changes every frame. The blue dot uses Perlin noise — notice how much smoother and more "natural" it wanders!
Math.random()
🔵 Perlin Noise — noise(t)
The canvas is split in half. Notice how the red dot zigzags wildly while the blue dot traces smooth, flowing curves.
💻 Using noise() in p5.js
p5.js includes the noise() function built-in — no imports needed.
Here's everything you need to know to use it:
Basic Syntax
// noise() accepts 1, 2, or 3 arguments (dimensions)
// and ALWAYS returns a value between 0.0 and 1.0
let n = noise(t); // 1D — one position in noise space
let n = noise(x, y); // 2D — two positions
let n = noise(x, y, z); // 3D — three positions
Classic Pattern: Smooth 1D Movement
let t = 0; // our "position" in noise space
function draw() {
// Sample the noise landscape at position t
let n = noise(t); // returns 0.0 – 1.0
// Map to something useful — e.g., an x position on canvas
let x = map(n, 0, 1, 0, width);
// Advance through noise space next frame
t += 0.01; // small step = smooth; larger step = more erratic
}
The 2D Movement Pattern (Used by SWBug!)
For 2D movement, we need two independent noise streams — one for the X direction and one for Y. We achieve this by starting each stream at a very different position in noise space (using offset seeds):
let tX = 0; // time offset for the X noise stream
let tY = 1000; // Y stream starts far away — so it's independent of X!
let x, y;
function setup() {
createCanvas(400, 400);
x = width / 2;
y = height / 2;
}
function draw() {
background(240);
// Get noise values in range [0, 1]
let nX = noise(tX);
let nY = noise(tY);
// Map [0, 1] → velocity in [-speed, speed]
let speed = 2;
let dx = map(nX, 0, 1, -speed, speed);
let dy = map(nY, 0, 1, -speed, speed);
// Move the dot
x += dx;
y += dy;
// Wrap around edges
x = (x + width) % width;
y = (y + height) % height;
// Draw the dot
fill(29, 78, 216);
noStroke();
ellipse(x, y, 12, 12);
// Advance through noise space — this is "noiseAmount" in SWBug!
tX += 0.01;
tY += 0.01;
}
SWBug class, the property
noiseAmount is exactly this step value — it controls how fast
_t advances through noise space each frame.
Small noiseAmount → smooth, lazy curves.
Large noiseAmount → faster, more erratic movement.
Key p5.js noise() Facts at a Glance
| Property | Value / Behavior |
|---|---|
| Return range | Always between 0.0 and 1.0 |
| Repeatability | noise(0.5) returns the same value on every call within a sketch run |
| Custom seed | Use noiseSeed(n) for reproducible results across runs |
| Detail / octaves | Use noiseDetail(octaves, falloff) to add finer texture (default: 4 octaves) |
| Dimensions | Accepts 1, 2, or 3 parameters — more dimensions give richer, more complex patterns |
📈 Demo 2: Visualizing 1D Noise as a Wave
This demo plots noise(t) values across the canvas width and scrolls
through time. The smooth blue line is Perlin
noise. The scattered red dots show what
random() looks like at the same scale — chaotic by comparison.
Drag the slider to change how quickly we travel through the noise landscape. Small value = slow, gentle waves. Large value = faster, more erratic.
🐛 Demo 3: 2D Perlin Movement — Just Like SWBug!
A dot moves using the exact same Perlin noise technique as SWBug's
'p' mode. Adjust noiseAmount and speed to see how
each parameter shapes the movement. The fading trail shows the path traced over time.
Small noiseAmount = lazy, sweeping curves. Large noiseAmount = faster direction changes. Speed controls how far the dot travels per frame.
The Code Behind This Demo
Here's the core logic — compare it with the SWBug source code and you'll see they're identical in approach:
let x, y;
let tX, tY; // independent time offsets for X and Y
let noiseAmt = 0.01; // how fast we move through noise space
let speed = 2; // how far we move per frame
function setup() {
createCanvas(400, 300);
x = width / 2;
y = height / 2;
// Random seeds so each run looks different
tX = random(1000);
tY = random(1000); // Y starts far from X — keeps them independent!
}
function draw() {
background(245, 245, 245, 30); // semi-transparent = fading trail
// ── THE PERLIN NOISE MOVEMENT ──
tX += noiseAmt; // advance through noise space
tY += noiseAmt;
let nx = noise(tX); // get smooth value in [0, 1]
let ny = noise(tY);
// Map [0, 1] to velocity in [-speed, speed]
let dx = (nx - 0.5) * 2 * speed;
let dy = (ny - 0.5) * 2 * speed;
x += dx;
y += dy;
// Wrap around edges
if (x < 0) x = width;
if (x > width) x = 0;
if (y < 0) y = height;
if (y > height) y = 0;
// Draw the dot
fill(29, 78, 216);
noStroke();
ellipse(x, y, 12, 12);
}
noise() returns values in [0, 1].
We want velocities centered on zero (so the dot moves in all directions).
Subtracting 0.5 shifts the range to [−0.5, 0.5].
Multiplying by 2 gives [−1, 1].
Multiplying by speed gives [−speed, speed].
That is exactly what SWBug does!
🐞 How SWBug Uses Perlin Noise
Now that you understand Perlin noise, you can read the actual SWBug source code and
everything makes sense. Here's the Perlin mode from swBug.js (lightly annotated):
// Inside SWBug's move() method — 'p' mode (Perlin noise):
this._t += this.noiseAmount; // Advance time (small = smooth, large = erratic)
// Sample two INDEPENDENT noise streams (different seed offsets)
const nx = noise(this._noiseSeedX + this._t);
const ny = noise(this._noiseSeedY + this._t);
// Map [0, 1] → velocity in [-speed, speed]
const dx = (nx - 0.5) * 2 * this.speed;
const dy = (ny - 0.5) * 2 * this.speed;
// Update position in user-space coordinates
this.x += dx;
this.y += dy;
Each SWBug is created with a unique random
_noiseSeedX and _noiseSeedY,
so multiple bugs all start at different points in the noise landscape and wander independently —
even though they're all calling the same noise() function.
✅ Key Takeaways
- Perlin noise was invented by Ken Perlin in the early 1980s for Hollywood computer graphics, and earned him an Academy Award in 1997.
-
Unlike
Math.random(), Perlin noise produces values that change smoothly — nearby input values always produce nearby output values. -
In p5.js,
noise(t)always returns a value between 0.0 and 1.0. Usemap()to translate it to whatever range you need. -
Use two separate noise streams (independent seed offsets for X and Y)
to produce smooth 2D movement — the exact technique powering
SWBug. -
The step size (how much you increase
tper frame) controls smoothness: small step → gentle curves; large step → faster, more erratic. - Perlin noise powers terrain generation, cloud textures, fire effects, procedural animation, game worlds, and much more — it's one of the most useful tools in a creative coder's toolkit.
Next Steps
- Explore the SWBug Class Reference to see all the ways you can configure Perlin noise movement.
- Open the SWBug Demo and experiment with the noiseAmount and speed controls with multiple bugs.
-
Try this: what happens if you replace two 1D noise calls with a single
noise(tX, tY)(2D noise)? The paths look different! - Look up noiseDetail() in the p5.js reference to learn about octaves — adding finer layers of detail to your noise.