Randomness & Noise
random() vs Perlin noise — controlled chaos for organic-feeling generative visuals.
random()
random() returns a pseudo-random floating-point number:
random() // 0 to 1 (exclusive)
random(10) // 0 to 10
random(5, 15) // 5 to 15
random(-1, 1) // -1 to 1
Pick a random element from an array:
let colours = ['#ff6432', '#3b82f6', '#22c55e', '#f59e0b', '#c084fc'];
let c = random(colours);
fill(c);
The problem with random()
Pure randomness is noisy — visually jarring. This grid of random sizes looks scattered:
function setup() {
createCanvas(600, 400);
noLoop();
}
function draw() {
background(20);
noStroke();
fill(100, 200, 255, 150);
for (let x = 20; x < width; x += 30) {
for (let y = 20; y < height; y += 30) {
circle(x, y, random(5, 25)); // chaotic jump between sizes
}
}
}
Each circle is independent of its neighbours. The result is visual static, not an organic field.
noise() — Perlin noise
noise(x) returns a smooth random value between 0 and 1. Adjacent inputs produce adjacent outputs — values flow smoothly rather than jumping wildly.
// Compare
random() // 0.83
random() // 0.12 ← jumps anywhere
random() // 0.67
noise(0.0) // 0.52
noise(0.01) // 0.53 ← smooth transition
noise(0.02) // 0.54
Noise-driven animation
let t = 0;
function draw() {
background(20);
noStroke();
fill(100, 200, 255);
let x = map(noise(t), 0, 1, 0, width);
let y = map(noise(t + 100), 0, 1, 0, height); // +100 to use a different region
circle(x, y, 30);
t += 0.01; // move through noise space slowly
}
The + 100 offset is important: using the same t value for both x and y would make them mirror each other. Use different offsets for each independent dimension.
Noise field grid
function setup() {
createCanvas(600, 400);
noLoop();
}
function draw() {
background(20);
noStroke();
for (let x = 20; x < width; x += 25) {
for (let y = 20; y < height; y += 25) {
let nx = x / 200; // scale controls how "zoomed in" the noise is
let ny = y / 200;
let n = noise(nx, ny); // 2D noise
let sz = map(n, 0, 1, 4, 22);
let hue = map(n, 0, 1, 180, 320);
colorMode(HSB, 360, 100, 100);
fill(hue, 70, 90, 80);
circle(x, y, sz);
}
}
}
Change 200 to a smaller number (like 100) to zoom in — the shapes grow smoother and larger. A larger number (like 400) gives more variation.
3D noise for animation
Pass a third argument t to animate the noise field over time:
function draw() {
background(15);
noStroke();
let t = frameCount * 0.003;
for (let x = 15; x < width; x += 25) {
for (let y = 15; y < height; y += 25) {
let n = noise(x / 200, y / 200, t);
let sz = map(n, 0, 1, 2, 20);
colorMode(HSB, 360, 100, 100);
fill(map(n, 0, 1, 200, 300), 70, 90);
circle(x, y, sz);
}
}
}
noiseSeed() and randomSeed()
Both noise() and random() are deterministic given a seed — the same seed always produces the same sequence:
function setup() {
createCanvas(600, 400);
noiseSeed(42); // fix the noise pattern
randomSeed(99); // fix random() sequence
noLoop();
}
This is essential for reproducible generative art — you can save an interesting seed number and regenerate the exact same image.
Flow fields
A flow field assigns a direction (angle) to every point in space using noise. Particles follow these directions:
let particles = [];
const NUM = 300;
const SCALE = 0.004;
function setup() {
createCanvas(700, 500);
background(15);
for (let i = 0; i < NUM; i++) {
particles.push({
x: random(width),
y: random(height),
hue: random(180, 300)
});
}
}
function draw() {
// Slow fade instead of clear — creates trails
background(15, 15, 20, 8);
colorMode(HSB, 360, 100, 100, 100);
for (let p of particles) {
let angle = noise(p.x * SCALE, p.y * SCALE) * TWO_PI * 2;
p.x += cos(angle) * 1.5;
p.y += sin(angle) * 1.5;
// Wrap around edges
if (p.x < 0) p.x = width;
if (p.x > width) p.x = 0;
if (p.y < 0) p.y = height;
if (p.y > height) p.y = 0;
stroke(p.hue, 60, 90, 40);
strokeWeight(1);
point(p.x, p.y);
}
}
Gaussian (normal) distribution
randomGaussian() returns values clustered around 0, with a bell-curve distribution — things close to the centre are more likely:
// randomGaussian(mean, standardDeviation)
let x = randomGaussian(width / 2, 80); // clustered around centre
Use it for effects where most particles should be near a focal point, with a few outliers.
Key takeaways
random()gives unpredictable jumps;noise()gives smooth, organic variationnoise(x, y, t)takes 1, 2, or 3 coordinates — use multiple offsets for independent dimensions- Noise scale controls zoom: small values = smooth/slow variation, large = fine/rapid
randomSeed()andnoiseSeed()make sequences reproducible- Flow fields use noise to assign directions and steer particles organically
randomGaussian()creates normally-distributed values for more natural-looking scatter