Vary a drawing with randomness, waves, and noise.
Choose the character of the variation
Scrub one input through three patterns.
The horizontal axis is input; height is the resulting value, from 0 to 1. The circles use that value for size.
Explore scale, amount, and cosine
Input scale changes how much of the pattern fits across the row. Variation amount changes how far the values push the marks, without changing the pattern itself. Cosine is sine shifted by a quarter cycle.
Try it: . The curves still differ; every mark becomes the same size.
p5.js code: the three rows
Comments identify code that belongs outside the sketch functions, in setup(), or in draw(). Canvas creation, background clearing, styling, and function wrappers are omitted. Sine and cosine use p5.js’s default radians. The snippets draw the marks and reproduce the behavior, not the exact samples of these canvases; axes, curves, and the cursor are omitted.
Fixed random samples
// Outside setup() and draw():
let samples;
// In setup():
randomSeed(42);
samples = Array.from({ length: 49 }, () => random());
// In draw():
const inputScale = 1;
const amount = 0.8;
for (let i = 0; i < 25; i++) {
const u = (i / 24) * 12 * inputScale;
const value = samples[floor(u * 4)];
const diameter = 14 + (value - 0.5) * 22 * amount;
circle(map(i, 0, 24, 24, width - 24), 40, diameter);
} Sine / cosine
// In draw():
const inputScale = 1;
const amount = 0.8;
for (let i = 0; i < 25; i++) {
const u = (i / 24) * 12 * inputScale;
const value = (1 + sin(u)) / 2;
const diameter = 14 + (value - 0.5) * 22 * amount;
circle(map(i, 0, 24, 24, width - 24), 40, diameter);
}
// Replace sin(u) with cos(u) for a quarter-cycle shift. Perlin noise
// In setup():
noiseSeed(42);
// In draw():
const inputScale = 1;
const amount = 0.8;
for (let i = 0; i < 25; i++) {
const u = (i / 24) * 12 * inputScale;
const value = noise(u);
const diameter = 14 + (value - 0.5) * 22 * amount;
circle(map(i, 0, 24, 24, width - 24), 40, diameter);
} Keep irregularity without flicker
These grids begin with irregular sizes. Step forward one frame to see which sizes change. Play to compare abrupt changes with a smoothly evolving field.
Try it: play, then restart and play again. Restart returns to frame 0 with the same sequence, so the same seed repeats the same flicker, frame for frame.
Playback runs at 30 steps per second and pauses after 10 seconds.
p5.js code: Fresh random values
Comments identify code that belongs outside the sketch functions, in setup(), or in draw(). Canvas creation, styling, and function wrappers are omitted.
// In setup():
randomSeed(42);
// In draw():
for (let row = 0; row < 7; row++) {
for (let column = 0; column < 6; column++) {
const value = random();
circle(20 + column * 40, 20 + row * 30, 6 + value * 20);
}
} p5.js code: Retained random values
// Outside setup() and draw():
let values;
// In setup():
randomSeed(42);
values = Array.from({ length: 42 }, () => random());
// In draw():
for (let row = 0; row < 7; row++) {
for (let column = 0; column < 6; column++) {
const value = values[row * 6 + column];
circle(20 + column * 40, 20 + row * 30, 6 + value * 20);
}
} p5.js code: Moving through noise
// In setup():
noiseSeed(42);
// In draw():
const seconds = millis() / 1000;
for (let row = 0; row < 7; row++) {
for (let column = 0; column < 6; column++) {
const value = noise(column * 0.6, row * 0.6, seconds);
circle(20 + column * 40, 20 + row * 30, 6 + value * 20);
}
} Retained random values are useful for persistent differences between objects. Waves suit deliberate pulses and cycles. Noise suits related variation across a surface or through time. None is universally more natural: choose the kind of regularity the drawing needs.
Make one mark change with time
Scrub time to change the circle’s size, then press Play. The noise function and size mapping stay fixed.
Playback starts paused and stops at 20 seconds; pressing Play at the end starts over. Scrubbing pauses playback and redraws at that time.
Give each mark its own input
Both grids use the same noise function and size mapping. Only the inputs differ.
Try it: play, then pause. Which grid still has different sizes?
Explore the combinations
Choose an input source, a variation function, and a shape property. Try mouse or touch instead of time, sine or fixed random instead of noise, or position instead of size. To yoke a property to an input is to make it follow that input.
Playback stops at 20 seconds. Scrub or rewind with the time slider.
Move or drag over the grid, or use these sliders with a keyboard.
Connection diagram
Click a source, function, or output box to select it; keyboard users can focus a box and press Enter or Space. “Position + time” connects those inputs together.
- Source
- Scale
- Function
- Amount
- Property
Try it: compare Time with Position + time during playback. Which makes all marks change together?
Scale multiplies each active input. Noise and fixed random use separate coordinates; sine adds them. Position modes show faint anchor marks at the unshifted grid locations.
p5.js code: selected method and input
The code follows the controls. Drag an underlined number horizontally, or focus it and use arrow keys, to change the matching slider. Shift + arrow takes larger steps. Comments identify code that belongs outside the sketch functions, in setup(), or in draw(); canvas creation, background clearing, styling, and function wrappers are omitted. In a sketch, millis() supplies time and mouseX/mouseY supply pixel coordinates; this page normalizes the pointer to run from 0 to 1 across the canvas and multiplies by 10 to explore a larger input range.
// In setup():
noiseSeed(42);
// In draw():
const inputScale = 0.6;
const amount = 0.9;
for (let row = 0; row < 8; row++) {
for (let column = 0; column < 18; column++) {
const u = column * inputScale;
const v = row * inputScale;
const value = noise(u, v);
const diameter = 20 + (value - 0.5) * 34 * amount;
circle(20 + column * 40, 30 + row * 40, diameter);
}
} The expressions use normalized values: wave(u) = (1 + sin(u)) / 2. fixedRandom(x, y, z) retrieves a seeded random value for a coordinate cell; it is a helper in this demonstration, not a p5.js function. Its cache keeps one sample per cell for the life of the sketch, so with the same seed, visiting cells in the same order reproduces the samples. Run initialization once, not on every frame. The noise demonstration uses single-octave gradient Perlin noise; p5.js noise() has different implementation details, but the same useful nearby-input behavior. The snippet reproduces the behavior, not the exact noise or random samples of this canvas.
Going beyond: other inputs
Time, position, and the mouse are starting points. In creative coding and physical computing, the same connection can begin with any value your program can read:
- Interaction
- Keyboard keys, touch gestures, game-controller buttons and sticks.
- Physical sensors
- Light, distance, pressure, temperature, or motion from an accelerometer.
- Sound
- Microphone level, estimated pitch, or detected beats. Extract a useful feature from the audio before mapping it to a property.
- Messages and data
- MIDI notes and controls, OSC or other network messages, weather observations, or values read from a dataset.
- The program itself
- A counter, a simulation variable, or another object's position.
The pattern stays the same: read an input → transform its range → drive a visible property. A distance sensor could control spacing between marks; microphone level could control their size. Neither needs to pass through a random, sine, or noise function first.
Working with sensors. Check the units and calibrate the useful range, then normalize it, for example to 0–1. Keep the most recent reading between updates rather than assuming a new sample arrives on every drawing frame. Smooth noisy continuous readings when useful, accepting the added lag; for switches or button presses, detecting a change may be more useful than smoothing.
Use these choices in a sketch
- Animation and Randomness: retain values with arrays, use
setup()anddraw(), and animate with noise. Also available in Processing. - Explore parameter spaces: yoke a value to the mouse, find a setting you like, and capture it.
- Sine in Creative Coding: waves, circles, and periodic motion.
- Arranging Items in a Grid: build the row and column coordinates that drive spatial variation.