Perlin Noise Magic: Creating Beautiful, Organic Motion
Where creativity meets code
Welcome back to Building Creative Machines! Today, we're diving into something magical—an invisible force that can turn random noise into mesmerizing, flowing patterns. This force is called Perlin Noise. But don't worry, we're not getting too technical—this is all about the beauty and simplicity of what you can create, even if you're not a coder or an engineer.
What is Perlin Noise?
Imagine you’re looking at a gently rolling landscape or the swirls of smoke in the air. Perlin Noise is great at mimicking those natural, smooth transitions. Unlike chaotic, random noise, Perlin Noise generates patterns that feel organic, almost like something nature would create.
Bringing It to Life
We developed a simple piece of software to show you how Perlin Noise works its magic. Picture a bunch of particles moving around on a screen, leaving trails behind them as they go. Instead of moving randomly, these particles follow invisible pathways Perlin Noise generates. The result? Gorgeous, flowing designs that are never quite the same twice.
In the software, each particle reacts to the Perlin Noise fields, and we can even attract them to specific points by clicking with the mouse (not on this demo vídeo but here). This creates patterns that look alive—like they're breathing or following some mysterious current.
Why This Matters
This isn’t just a cool visual trick. Perlin Noise is used in everything from movie animations to video games and even in the design of artificial landscapes. It helps creators generate natural-looking effects without having to design every detail manually.
Whether you’re a marketer looking to understand how creative tech can be applied in branding or a business leader curious about the intersections of technology and creativity, understanding tools like Perlin Noise opens up new possibilities. It’s a reminder that sometimes, the most complex patterns come from the most straightforward rules.
See It in Action
Watch a live demo of the software in action right here.
Try It for Yourself
If you’re curious about how this works, we’ve shared the code below. Even if you're not a coder, it’s worth a peek—you might be surprised at how simple it is to start creating your own digital magic.
let scl = 20;
let cols, rows;
let zoff = 0;
let particles = [];
let flowfield;
let attractors = []; // Array to hold attractor points
function setup() {
createCanvas(800, 800);
colorMode(HSB, 255);
cols = floor(width / scl);
rows = floor(height / scl);
flowfield = new Array(cols * rows);
for (let i = 0; i < 500; i++) {
particles[i] = new Particle();
}
background(51);
}
function draw() {
let yoff = 0;
for (let y = 0; y < rows; y++) {
let xoff = 0;
for (let x = 0; x < cols; x++) {
let index = x + y * cols;
let angle = noise(xoff, yoff, zoff) * TWO_PI * 4;
let v = p5.Vector.fromAngle(angle);
v.setMag(1);
flowfield[index] = v;
xoff += 0.1;
}
yoff += 0.1;
}
zoff += 0.01;
for (let i = 0; i < particles.length; i++) {
particles[i].follow(flowfield);
particles[i].update();
particles[i].edges();
particles[i].show();
}
// If mouse is pressed, create new particles at mouse position
if (mouseIsPressed) {
particles.push(new Particle(mouseX, mouseY, random(255)));
}
// Apply attraction force
attractors.forEach(attractor => {
particles.forEach(particle => {
particle.attract(attractor);
});
});
}
// When mouse is clicked, create a new attractor at mouse position
function mouseClicked() {
attractors.push(createVector(mouseX, mouseY));
}
function Particle(x, y, hue) {
if (x && y) {
// If position is given, use that
this.pos = createVector(x, y);
} else {
// Else, start at random position
this.pos = createVector(random(width), random(height));
}
this.vel = createVector(0, 0);
this.acc = createVector(0, 0);
this.maxspeed = 2;
// If hue is given, use that
this.hue = hue || random(255);
this.prevPos = this.pos.copy();
this.update = function() {
this.vel.add(this.acc);
this.vel.limit(this.maxspeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.follow = function(flowfield) {
let x = floor(this.pos.x / scl);
let y = floor(this.pos.y / scl);
let index = x + y * cols;
let force = flowfield[index];
this.applyForce(force);
}
this.applyForce = function(force) {
this.acc.add(force);
}
this.show = function() {
stroke(this.hue, 255, 255, 25);
this.hue = this.hue + 1 % 255;
strokeWeight(1);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function() {
this.prevPos.x = this.pos.x;
this.prevPos.y = this.pos.y;
}
this.edges = function() {
if (this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if (this.pos.x < 0) {
this.pos.x = width;
this.updatePrev();
}
if (this.pos.y > height) {
this.pos.y = 0;
this.updatePrev();
}
if (this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
// Attraction to a point
this.attract = function(target) {
let force = p5.Vector.sub(target, this.pos); // A vector pointing from the position to the target
let dsquared = force.magSq(); // Distance squared
dsquared = constrain(dsquared, 25, 500); // Limiting the distance to eliminate "extreme" results for very close or very far objects
const G = 50; // Gravitational constant
let strength = G / dsquared; // Calculate gravitation magnitude
force.setMag(strength);
this.acc.add(force);
}
}If you like, you can visit dozens of experiences like this here, mainly the one we highlighted on Substack about the visualisation of Pi.

