Processing Beginner Article 8

Randomness & Noise

Use random() and Perlin noise to generate organic, varied visuals with controlled unpredictability.

⏱ 18 min random noise Perlin noiseSeed map organic terrain

random(): uniform random numbers

random(min, max) returns a float chosen uniformly at random in the range [min, max). The result is different every time — including every time you restart the sketch.

void draw() {
  // Random position, size, and colour for each dot
  float x = random(width);
  float y = random(height);
  float r = random(4, 30);
  fill(random(255), random(100, 200), 180);
  noStroke();
  ellipse(x, y, r, r);
}

random(n) is shorthand for random(0, n).

To pick a random integer index, use int(random(n)) or floor(random(n)):

color[] palette = { #E63946, #457B9D, #1D3557, #A8DADC };
fill(palette[int(random(palette.length))]);

randomSeed: reproducible results

By default, the random number sequence is different every run. Pass a seed to randomSeed() to lock it to a specific sequence — useful for generating the same layout every time:

void setup() {
  size(600, 400);
  randomSeed(42);         // same sequence every run
  background(15);
  noStroke();

  for (int i = 0; i < 200; i++) {
    fill(random(255), random(255), random(255), 150);
    float x = random(width);
    float y = random(height);
    float r = random(5, 40);
    ellipse(x, y, r, r);
  }
}

Change 42 to any other integer to get a completely different but equally reproducible layout.

noise(): Perlin noise

random() produces “spiky” results — consecutive values can jump wildly. Perlin noise produces a smoothly varying sequence that feels more natural. Processing’s noise() returns a value between 0.0 and 1.0.

float t = 0;

void draw() {
  background(20);
  float n = noise(t);                   // 0.0 – 1.0
  float x = map(n, 0, 1, 50, width - 50);
  fill(100, 180, 255);
  noStroke();
  ellipse(x, height / 2, 40, 40);
  t += 0.01;                            // step through noise space slowly
}

The key insight: the step size controls how fast the noise changes. Small steps (0.005–0.02) produce slow, smooth movement. Large steps (0.2+) produce jittery, almost-random movement.

noise(x, y): 2D noise field

Pass two arguments to sample a 2D slice of the noise field. Use this to set colours, heights, or positions across a grid:

float SCALE = 0.008;  // zoom level of the noise

void setup() {
  size(600, 600);
  noStroke();
  loadPixels();

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      float n = noise(x * SCALE, y * SCALE);
      float bright = map(n, 0, 1, 0, 255);
      pixels[y * width + x] = color(bright * 0.3, bright * 0.5, bright);
    }
  }
  updatePixels();
}

Smaller SCALE values zoom in, producing large smooth blobs. Larger values zoom out, producing finer texture.

noise(x, y, z): animated noise

The third argument adds a time dimension. Increment z each frame to animate the field:

float SCALE = 0.006;
float timeStep = 0;

void draw() {
  loadPixels();
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      float n = noise(x * SCALE, y * SCALE, timeStep);
      float h = map(n, 0, 1, 0, 255);
      pixels[y * width + x] = color(h * 0.2, h * 0.6, h);
    }
  }
  updatePixels();
  timeStep += 0.005;
}

noiseSeed: reproducible noise

Just like randomSeed(), noiseSeed(n) locks the noise field to a fixed pattern:

noiseSeed(99);  // same landscape every run

map(): translating noise into useful ranges

noise() always returns 0–1. The map() function stretches or shrinks that range to whatever you need:

float n = noise(t);

// Map to a y position between 100 and 500
float y = map(n, 0, 1, 100, 500);

// Map to a speed between 0.5 and 4.0
float speed = map(n, 0, 1, 0.5, 4.0);

// Map to a colour channel
float red = map(n, 0, 1, 80, 220);

map(value, fromLow, fromHigh, toLow, toHigh) — memorise this signature.

Full example: noise-based terrain line

A rolling landscape silhouette, updated every frame to scroll left:

float[] terrain;
int POINTS = 200;
float xOff = 0;

void setup() {
  size(800, 500);
  terrain = new float[POINTS];
}

void draw() {
  background(15, 15, 30);

  // Build terrain heights from noise
  float t = xOff;
  for (int i = 0; i < POINTS; i++) {
    terrain[i] = map(noise(t), 0, 1, height * 0.3, height * 0.85);
    t += 0.018;
  }

  // Draw filled terrain shape
  noStroke();
  fill(40, 80, 60);
  beginShape();
  vertex(0, height);
  for (int i = 0; i < POINTS; i++) {
    float x = map(i, 0, POINTS - 1, 0, width);
    vertex(x, terrain[i]);
  }
  vertex(width, height);
  endShape(CLOSE);

  // Draw glowing ridge line
  stroke(80, 200, 120);
  strokeWeight(2);
  noFill();
  beginShape();
  for (int i = 0; i < POINTS; i++) {
    float x = map(i, 0, POINTS - 1, 0, width);
    vertex(x, terrain[i]);
  }
  endShape();

  // Scroll by advancing the noise offset
  xOff += 0.005;
}

The landscape scrolls because xOff increases each frame, sliding the window through noise space. Every run produces the same initial terrain if you add noiseSeed(1) to setup().

Key takeaways

  • random(min, max) gives uniformly distributed values; consecutive results can jump anywhere.
  • randomSeed(n) makes random() reproducible.
  • noise(x) gives smoothly interpolated values — ideal for organic motion.
  • Add a second argument noise(x, y) for 2D fields; a third noise(x, y, z) for animation over time.
  • Always run noise output through map() to translate the 0–1 range into useful numbers.
  • noiseSeed(n) fixes the noise field for reproducible generations.