Are you ready to make something truly beautiful with p5.js? Forget about boring bar charts and sales data—let’s create art that moves, breathes, and responds to your touch. We’re going to explore generative art, where code becomes your paintbrush and algorithms become your muse.
Generative art is all about creating beauty through systems and rules. Instead of drawing each line by hand, you write code that follows patterns, and those patterns create something unexpected and often stunning. It’s like planting a garden and watching it grow—you set the conditions, but nature (or in this case, your code) does the rest.
Let’s start with something simple but mesmerizing: a field of flowers that sway in the wind. Each flower will be unique, with its own color, size, and movement pattern. We’ll use mathematical functions to create organic, natural-looking motion.
let flowers = [];
let wind = 0;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);// Create a field of flowersfor (let i = 0; i < 50; i++) {flowers.push(new Flower(random(width),random(height),random(20, 60),random(360)));}
}function draw() {background(200, 30, 95, 0.1); // Soft sky blue with fade effect// Update windwind = sin(frameCount * 0.02) * 0.5;// Draw and update flowersfor (let flower of flowers) {flower.update();flower.display();}
}class Flower {constructor(x, y, size, hue) {this.x = x;this.y = y;this.size = size;this.hue = hue;this.angle = 0;this.stemLength = size * 2;this.petals = floor(random(5, 12));}update() {this.angle += wind + sin(frameCount * 0.05 + this.x * 0.01) * 0.1;}display() {push();translate(this.x, this.y);rotate(this.angle);// Draw stemstroke(120, 80, 40);strokeWeight(3);line(0, 0, 0, this.stemLength);// Draw flower headtranslate(0, this.stemLength);// Draw petalsfor (let i = 0; i < this.petals; i++) {let angle = (i / this.petals) * TWO_PI;let petalX = cos(angle) * this.size * 0.8;let petalY = sin(angle) * this.size * 0.8;fill(this.hue, 80, 90, 0.8);noStroke();ellipse(petalX, petalY, this.size * 0.6, this.size * 0.3);}// Draw centerfill(60, 90, 90);ellipse(0, 0, this.size * 0.3);pop();}
}
This creates a gentle field of flowers that sway in a simulated breeze. Each flower is unique, with different colors, sizes, and numbers of petals. The HSB
color mode gives us more intuitive control over colors—hue (the color), saturation (how vivid), and brightness (how light or dark).
But let’s make it more interactive. What if the flowers respond to your mouse, growing taller when you’re near them?
let flowers = [];
let wind = 0;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);for (let i = 0; i < 50; i++) {flowers.push(new Flower(random(width),random(height),random(20, 60),random(360)));}
}function draw() {background(200, 30, 95, 0.1);wind = sin(frameCount * 0.02) * 0.5;for (let flower of flowers) {flower.update();flower.display();}
}class Flower {constructor(x, y, size, hue) {this.x = x;this.y = y;this.size = size;this.hue = hue;this.angle = 0;this.stemLength = size * 2;this.petals = floor(random(5, 12));this.targetStemLength = this.stemLength;}update() {this.angle += wind + sin(frameCount * 0.05 + this.x * 0.01) * 0.1;// Respond to mouse proximitylet distance = dist(mouseX, mouseY, this.x, this.y);let mouseInfluence = map(distance, 0, 100, 1.5, 1);this.targetStemLength = this.size * 2 * mouseInfluence;// Smooth animationthis.stemLength += (this.targetStemLength - this.stemLength) * 0.1;}display() {push();translate(this.x, this.y);rotate(this.angle);// Draw stem with gradientfor (let i = 0; i < this.stemLength; i += 5) {let alpha = map(i, 0, this.stemLength, 1, 0.3);stroke(120, 80, 40, alpha);strokeWeight(3);line(0, i, 0, i + 5);}translate(0, this.stemLength);// Draw petals with variationfor (let i = 0; i < this.petals; i++) {let angle = (i / this.petals) * TWO_PI;let petalX = cos(angle) * this.size * 0.8;let petalY = sin(angle) * this.size * 0.8;// Vary petal color slightlylet petalHue = this.hue + sin(i * 0.5) * 20;fill(petalHue, 80, 90, 0.8);noStroke();ellipse(petalX, petalY, this.size * 0.6, this.size * 0.3);}fill(60, 90, 90);ellipse(0, 0, this.size * 0.3);pop();}
}
Now the flowers grow taller when you move your mouse near them! The map()
function is crucial here—it takes a value from one range (distance from 0 to 100 pixels) and converts it to another range (stem length multiplier from 1.5 to 1).
Let’s take this further and create something more abstract and mesmerizing. How about a flowing particle system that creates organic, living patterns?
let particles = [];
let flowField = [];function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);// Create flow fieldlet cols = 20;let rows = 15;let fieldWidth = width / cols;let fieldHeight = height / rows;for (let x = 0; x < cols; x++) {flowField[x] = [];for (let y = 0; y < rows; y++) {let angle = noise(x * 0.1, y * 0.1, 0) * TWO_PI * 2;flowField[x][y] = angle;}}// Create particlesfor (let i = 0; i < 100; i++) {particles.push(new Particle(random(width), random(height)));}
}function draw() {background(0, 0, 0, 0.1);// Update flow fieldlet cols = 20;let rows = 15;for (let x = 0; x < cols; x++) {for (let y = 0; y < rows; y++) {let angle = noise(x * 0.1, y * 0.1, frameCount * 0.01) * TWO_PI * 2;flowField[x][y] = angle;}}// Update and display particlesfor (let particle of particles) {particle.follow(flowField);particle.update();particle.display();}
}class Particle {constructor(x, y) {this.pos = createVector(x, y);this.vel = createVector(0, 0);this.acc = createVector(0, 0);this.maxSpeed = 2;this.hue = random(360);this.life = 255;}follow(field) {let x = floor(this.pos.x / (width / 20));let y = floor(this.pos.y / (height / 15));x = constrain(x, 0, 19);y = constrain(y, 0, 14);let angle = field[x][y];let force = p5.Vector.fromAngle(angle);force.setMag(0.1);this.acc.add(force);}update() {this.vel.add(this.acc);this.vel.limit(this.maxSpeed);this.pos.add(this.vel);this.acc.mult(0);// Wrap around edgesif (this.pos.x > width) this.pos.x = 0;if (this.pos.x < 0) this.pos.x = width;if (this.pos.y > height) this.pos.y = 0;if (this.pos.y < 0) this.pos.y = height;// Gradually fadethis.life -= 0.5;if (this.life <= 0) {this.pos = createVector(random(width), random(height));this.life = 255;this.hue = (this.hue + 30) % 360;}}display() {stroke(this.hue, 80, 90, this.life / 255);strokeWeight(2);point(this.pos.x, this.pos.y);}
}
This creates a mesmerizing flow field where particles follow invisible currents that change over time. The noise()
function creates smooth, organic randomness that feels natural and flowing. Each particle leaves a trail as it moves, creating beautiful, ever-changing patterns.
But let’s make it even more interactive. What if you could paint with these particles, creating your own flow field with your mouse?
let particles = [];
let flowField = [];
let painting = false;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);// Initialize flow fieldlet cols = 40;let rows = 30;for (let x = 0; x < cols; x++) {flowField[x] = [];for (let y = 0; y < rows; y++) {flowField[x][y] = 0;}}for (let i = 0; i < 200; i++) {particles.push(new Particle(random(width), random(height)));}
}function draw() {background(0, 0, 0, 0.1);// Paint with mouseif (painting) {let cols = 40;let rows = 30;let x = floor(mouseX / (width / cols));let y = floor(mouseY / (height / rows));x = constrain(x, 0, cols - 1);y = constrain(y, 0, rows - 1);// Create a circular brushfor (let i = -2; i <= 2; i++) {for (let j = -2; j <= 2; j++) {let nx = x + i;let ny = y + j;if (nx >= 0 && nx < cols && ny >= 0 && ny < rows) {let angle = atan2(mouseY - pmouseY, mouseX - pmouseX);flowField[nx][ny] = angle;}}}}for (let particle of particles) {particle.follow(flowField);particle.update();particle.display();}
}function mousePressed() {painting = true;
}function mouseReleased() {painting = false;
}class Particle {constructor(x, y) {this.pos = createVector(x, y);this.vel = createVector(0, 0);this.acc = createVector(0, 0);this.maxSpeed = 3;this.hue = random(360);this.life = 255;}follow(field) {let cols = 40;let rows = 30;let x = floor(this.pos.x / (width / cols));let y = floor(this.pos.y / (height / rows));x = constrain(x, 0, cols - 1);y = constrain(y, 0, rows - 1);let angle = field[x][y];let force = p5.Vector.fromAngle(angle);force.setMag(0.2);this.acc.add(force);}update() {this.vel.add(this.acc);this.vel.limit(this.maxSpeed);this.pos.add(this.vel);this.acc.mult(0);if (this.pos.x > width) this.pos.x = 0;if (this.pos.x < 0) this.pos.x = width;if (this.pos.y > height) this.pos.y = 0;if (this.pos.y < 0) this.pos.y = height;this.life -= 1;if (this.life <= 0) {this.pos = createVector(random(width), random(height));this.life = 255;this.hue = (this.hue + 45) % 360;}}display() {stroke(this.hue, 90, 90, this.life / 255);strokeWeight(3);point(this.pos.x, this.pos.y);}
}
Now you can paint with particles! Click and drag to create flow currents that the particles will follow. It’s like digital finger painting with living, breathing particles.
Let’s create something completely different—a generative landscape that grows and evolves like a living organism. This will use cellular automata principles to create organic, natural-looking patterns.
let landscape = [];
let cols, rows;
let cellSize = 10;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);cols = width / cellSize;rows = height / cellSize;// Initialize landscape randomlyfor (let x = 0; x < cols; x++) {landscape[x] = [];for (let y = 0; y < rows; y++) {landscape[x][y] = random(1);}}
}function draw() {background(200, 30, 95);// Update landscapelet newLandscape = [];for (let x = 0; x < cols; x++) {newLandscape[x] = [];for (let y = 0; y < rows; y++) {let neighbors = countNeighbors(x, y);let current = landscape[x][y];// Conway's Game of Life rules with variationif (current > 0.5) {if (neighbors < 2 || neighbors > 3) {newLandscape[x][y] = max(0, current - 0.1);} else {newLandscape[x][y] = min(1, current + 0.05);}} else {if (neighbors === 3) {newLandscape[x][y] = min(1, current + 0.2);} else {newLandscape[x][y] = max(0, current - 0.02);}}}}landscape = newLandscape;// Draw landscapefor (let x = 0; x < cols; x++) {for (let y = 0; y < rows; y++) {let value = landscape[x][y];let hue = map(value, 0, 1, 120, 200); // Green to bluelet brightness = map(value, 0, 1, 30, 90);fill(hue, 80, brightness);noStroke();rect(x * cellSize, y * cellSize, cellSize, cellSize);}}// Add some organic growthif (random() < 0.1) {let x = floor(random(cols));let y = floor(random(rows));landscape[x][y] = min(1, landscape[x][y] + 0.3);}
}function countNeighbors(x, y) {let sum = 0;for (let i = -1; i < 2; i++) {for (let j = -1; j < 2; j++) {let nx = (x + i + cols) % cols;let ny = (y + j + rows) % rows;if (landscape[nx][ny] > 0.5) sum++;}}if (landscape[x][y] > 0.5) sum--;return sum;
}function mousePressed() {// Add life where you clicklet x = floor(mouseX / cellSize);let y = floor(mouseY / cellSize);if (x >= 0 && x < cols && y >= 0 && y < rows) {landscape[x][y] = 1;}
}
This creates a living landscape that grows, dies, and evolves according to simple rules. Click anywhere to add life, and watch as patterns emerge and change over time. It’s like watching a digital ecosystem grow and evolve.
Now let’s create something truly spectacular—a generative music visualizer that responds to sound. We’ll create a system that generates visual patterns based on frequency analysis, even without real audio input.
let particles = [];
let frequencies = [];
let time = 0;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);// Initialize frequency data (simulated)for (let i = 0; i < 64; i++) {frequencies[i] = 0;}// Create particle systemfor (let i = 0; i < 100; i++) {particles.push(new AudioParticle());}
}function draw() {background(0, 0, 0, 0.1);// Generate simulated frequency datafor (let i = 0; i < frequencies.length; i++) {let baseFreq = sin(time + i * 0.1) * 0.5 + 0.5;let mouseInfluence = map(dist(mouseX, mouseY, i * 10, height/2), 0, 200, 1, 0);frequencies[i] = baseFreq * mouseInfluence;}// Draw frequency barslet barWidth = width / frequencies.length;for (let i = 0; i < frequencies.length; i++) {let barHeight = frequencies[i] * height * 0.8;let hue = map(i, 0, frequencies.length, 0, 360);fill(hue, 80, 90, 0.6);noStroke();rect(i * barWidth, height - barHeight, barWidth - 1, barHeight);// Add glow effectdrawingContext.shadowBlur = 20;drawingContext.shadowColor = color(hue, 80, 90);rect(i * barWidth, height - barHeight, barWidth - 1, barHeight);drawingContext.shadowBlur = 0;}// Update and display particlesfor (let particle of particles) {particle.update(frequencies);particle.display();}time += 0.05;
}class AudioParticle {constructor() {this.pos = createVector(random(width), random(height));this.vel = createVector(0, 0);this.size = random(2, 8);this.hue = random(360);}update(freqs) {// Respond to frequency datalet freqIndex = floor(map(this.pos.x, 0, width, 0, freqs.length));freqIndex = constrain(freqIndex, 0, freqs.length - 1);let amplitude = freqs[freqIndex];this.vel.y = -amplitude * 5;this.pos.add(this.vel);this.vel.mult(0.95); // Damping// Wrap aroundif (this.pos.y < 0) this.pos.y = height;if (this.pos.x < 0) this.pos.x = width;if (this.pos.x > width) this.pos.x = 0;// Change color based on frequencythis.hue = (this.hue + amplitude * 10) % 360;}display() {fill(this.hue, 90, 90, 0.8);noStroke();ellipse(this.pos.x, this.pos.y, this.size, this.size);// Add trailfor (let i = 1; i < 5; i++) {let alpha = map(i, 1, 5, 0.8, 0);fill(this.hue, 90, 90, alpha);ellipse(this.pos.x - this.vel.x * i * 0.1, this.pos.y - this.vel.y * i * 0.1, this.size * 0.5, this.size * 0.5);}}
}
This creates a stunning audio visualizer that responds to your mouse position. The frequency bars represent different audio frequencies, and the particles float upward based on the intensity of each frequency. Move your mouse around to create different “sound” patterns.
The beauty of generative art is that you can combine these techniques in infinite ways. Want to create a galaxy that responds to music? Or a forest that grows based on your mouse movements? The possibilities are endless.
Here’s one more example that combines everything we’ve learned—a generative mandala that grows and evolves:
let mandala = [];
let angle = 0;
let growth = 0;function setup() {createCanvas(800, 600);colorMode(HSB, 360, 100, 100, 1);background(0);
}function draw() {translate(width/2, height/2);// Create rotating mandalafor (let i = 0; i < 12; i++) {push();rotate(angle + i * TWO_PI / 12);// Draw petalsfor (let j = 0; j < 8; j++) {let petalAngle = j * TWO_PI / 8;let x = cos(petalAngle) * (100 + growth);let y = sin(petalAngle) * (100 + growth);let hue = (angle * 10 + i * 30 + j * 45) % 360;fill(hue, 80, 90, 0.6);noStroke();push();translate(x, y);rotate(petalAngle);ellipse(0, 0, 30 + growth * 0.2, 60 + growth * 0.3);pop();}// Draw connecting linesstroke(angle * 5 % 360, 70, 80, 0.3);strokeWeight(2);for (let j = 0; j < 8; j++) {let angle1 = j * TWO_PI / 8;let angle2 = (j + 1) * TWO_PI / 8;let x1 = cos(angle1) * (100 + growth);let y1 = sin(angle1) * (100 + growth);let x2 = cos(angle2) * (100 + growth);let y2 = sin(angle2) * (100 + growth);line(x1, y1, x2, y2);}pop();}// Add floating particlesfor (let i = 0; i < 5; i++) {let particleAngle = angle * 0.5 + i * TWO_PI / 5;let particleRadius = 150 + sin(angle + i) * 50;let x = cos(particleAngle) * particleRadius;let y = sin(particleAngle) * particleRadius;fill((angle * 20 + i * 72) % 360, 90, 90, 0.8);noStroke();ellipse(x, y, 8, 8);}angle += 0.02;growth += 0.1;// Reset growth periodicallyif (growth > 100) {growth = 0;}
}function mousePressed() {// Add explosion effectfor (let i = 0; i < 20; i++) {let angle = random(TWO_PI);let radius = random(50, 200);let x = cos(angle) * radius;let y = sin(angle) * radius;fill(random(360), 90, 90, 0.8);noStroke();ellipse(x, y, random(5, 15), random(5, 15));}
}
This creates a living mandala that continuously grows, rotates, and evolves. Click anywhere to add explosion effects. The mandala uses mathematical symmetry to create beautiful, hypnotic patterns that feel both organic and geometric.
The key to creating compelling generative art is to start with simple rules and let complexity emerge naturally. Use mathematical functions like sin()
, cos()
, and noise()
to create organic movement. Experiment with color theory and transparency to create depth and atmosphere. And most importantly, make it interactive—let your audience become part of the artwork.
Remember, generative art isn’t about creating perfect, static images. It’s about creating living systems that surprise and delight you. Sometimes the most beautiful moments come from unexpected interactions between simple rules. So go ahead and experiment—let your code paint, let your algorithms dance, and see what beautiful patterns emerge from the chaos.