Creative Coding With P5js

views

title: 'Generative Art with p5.js: Creating Algorithmic Masterpieces' description: 'Explore the intersection of code, mathematics, and creativity to produce stunning visual art' category: 'Creative Coding' categorySlug: 'creative' difficulty: 'beginner' icon: 'solar:pallete-2-bold' gradient: 'linear-gradient(135deg, #f093fb, #f5576c)' publishDate: '2025-12-06' readTime: '35 min read' views: '6.5K' tags: 'p5.js', 'JavaScript', 'Generative Art', 'Creative Coding', 'Canvas', 'Animation' author: name: 'PlayHve' initials: 'PH' role: 'Tech Education Platform' bio: 'Your ultimate destination for cutting-edge technology tutorials. Learn AI, Web3, modern web development, and creative coding.'

Generative Art with p5.js: Creating Algorithmic Masterpieces

Explore the intersection of code, mathematics, and creativity to produce stunning visual art

Introduction

Generative art represents a fascinating convergence of programming, mathematics, and artistic expression. Unlike traditional digital art created with tools like Photoshop, generative art is created through algorithms - code that produces visual output based on rules, randomness, and mathematical principles.

In this comprehensive tutorial, we'll explore generative art using p5.js, a JavaScript library designed to make coding accessible for artists, designers, and educators. We'll start with fundamental concepts and progress to creating complex, beautiful artworks that you can mint as NFTs or use in interactive installations.

By the end of this guide, you'll understand core generative art techniques and have created several unique pieces, each generated from algorithmic rules that produce infinite variations.

Prerequisites

To follow along, you should have:

  • Basic JavaScript knowledge (variables, functions, loops)
  • Understanding of HTML and how to run JavaScript in a browser
  • Familiarity with coordinate systems and basic geometry
  • A code editor (VS Code recommended)
  • Modern web browser

Setting Up Your Creative Environment

Installing p5.js

Create a project folder and add these files:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Generative Art Studio</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
  <style>
    body {
      margin: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: #1a1a1a;
    }
    canvas {
      box-shadow: 0 10px 40px rgba(0,0,0,0.5);
    }
  </style>
</head>
<body>
  <script src="sketch.js"></script>
</body>
</html>

sketch.js:

function setup() {
  createCanvas(800, 800);
  background(255);
}

function draw() {
  // Your art will live here
}

Understanding the p5.js Canvas

The p5.js coordinate system starts at the top-left corner:

(0,0) €€€€€€€€€€€€€€€€€X
  
  
  
  
  
  Y

Center of 800x800 canvas: (400, 400)

Basic Shapes and Colors

function setup() {
  createCanvas(800, 800);
  background(20);
  
  // Set no outline
  noStroke();
  
  // RGB colors
  fill(255, 100, 150);
  circle(200, 200, 100);
  
  // HSB color mode (Hue, Saturation, Brightness)
  colorMode(HSB, 360, 100, 100);
  fill(180, 80, 90);
  rect(400, 200, 150, 150);
  
  // Transparent colors (alpha)
  fill(60, 70, 80, 50);
  ellipse(600, 400, 200, 300);
}

Your First Generative Pattern: Grid of Circles

Let's create a pattern where each circle has a random size and color:

function setup() {
  createCanvas(800, 800);
  background(240);
  noStroke();
  
  const cols = 10;
  const rows = 10;
  const spacing = width / cols;
  
  colorMode(HSB, 360, 100, 100);
  
  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) {
      const x = spacing * i + spacing / 2;
      const y = spacing * j + spacing / 2;
      
      // Random hue for each circle
      const hue = random(360);
      const size = random(20, 60);
      
      fill(hue, 80, 90, 0.7);
      circle(x, y, size);
    }
  }
}

Adding Interactive Randomness

Make it regenerate on mouse click:

function setup() {
  createCanvas(800, 800);
  colorMode(HSB, 360, 100, 100);
  generateArt();
}

function generateArt() {
  background(240);
  noStroke();
  
  const cols = 10;
  const rows = 10;
  const spacing = width / cols;
  
  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) {
      const x = spacing * i + spacing / 2;
      const y = spacing * j + spacing / 2;
      
      const hue = random(360);
      const size = random(20, 60);
      
      fill(hue, 80, 90, 0.7);
      circle(x, y, size);
    }
  }
}

function mousePressed() {
  generateArt();
}

Advanced Technique: Flow Fields

Flow fields create organic, flowing patterns by following vector forces:

let particles = [];
let flowField = [];
const cols = 50;
const rows = 50;
const scale = 20;

function setup() {
  createCanvas(800, 800);
  background(0);
  
  // Initialize flow field
  const noiseScale = 0.1;
  for (let y = 0; y < rows; y++) {
    for (let x = 0; x < cols; x++) {
      const angle = noise(x * noiseScale, y * noiseScale) * TWO_PI * 2;
      const v = p5.Vector.fromAngle(angle);
      v.setMag(0.5);
      flowField.push(v);
    }
  }
  
  // Create particles
  for (let i = 0; i < 1000; i++) {
    particles.push(new Particle());
  }
}

function draw() {
  // Fade effect instead of clearing
  fill(0, 5);
  rect(0, 0, width, height);
  
  // Update and display particles
  for (let particle of particles) {
    particle.follow(flowField);
    particle.update();
    particle.edges();
    particle.show();
  }
}

class Particle {
  constructor() {
    this.pos = createVector(random(width), random(height));
    this.vel = createVector(0, 0);
    this.acc = createVector(0, 0);
    this.maxSpeed = 2;
    this.prevPos = this.pos.copy();
    
    // Random color
    colorMode(HSB, 360, 100, 100);
    this.hue = random(360);
  }
  
  follow(flowField) {
    const x = floor(this.pos.x / scale);
    const y = floor(this.pos.y / scale);
    const index = x + y * cols;
    const force = flowField[index];
    
    if (force) {
      this.applyForce(force);
    }
  }
  
  applyForce(force) {
    this.acc.add(force);
  }
  
  update() {
    this.vel.add(this.acc);
    this.vel.limit(this.maxSpeed);
    this.pos.add(this.vel);
    this.acc.mult(0);
  }
  
  edges() {
    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();
    }
  }
  
  updatePrev() {
    this.prevPos.x = this.pos.x;
    this.prevPos.y = this.pos.y;
  }
  
  show() {
    stroke(this.hue, 80, 100, 0.5);
    strokeWeight(1);
    line(this.prevPos.x, this.prevPos.y, this.pos.x, this.pos.y);
    this.updatePrev();
  }
}

Recursive Patterns: Drawing Trees

Recursion creates beautiful fractal patterns:

function setup() {
  createCanvas(800, 800);
  background(20);
  stroke(255);
  strokeWeight(2);
  
  // Start from bottom center
  translate(width / 2, height);
  branch(150);
}

function branch(len) {
  // Draw the branch
  line(0, 0, 0, -len);
  
  // Move to the end of the branch
  translate(0, -len);
  
  if (len > 4) {
    push();
    rotate(PI / 6);
    branch(len * 0.67);
    pop();
    
    push();
    rotate(-PI / 6);
    branch(len * 0.67);
    pop();
  }
}

Animated Growing Tree

let angle = 0;
let maxDepth = 8;
let currentDepth = 0;

function setup() {
  createCanvas(800, 800);
  colorMode(HSB, 360, 100, 100);
  frameRate(30);
}

function draw() {
  background(20);
  
  stroke(120, 60, 100);
  strokeWeight(2);
  
  translate(width / 2, height);
  
  angle = map(sin(frameCount * 0.02), -1, 1, PI / 8, PI / 3);
  currentDepth = floor(map(sin(frameCount * 0.01), -1, 1, 1, maxDepth));
  
  branch(120, currentDepth);
}

function branch(len, depth) {
  if (depth === 0) return;
  
  const hue = map(depth, 0, maxDepth, 30, 120);
  stroke(hue, 80, 100);
  
  line(0, 0, 0, -len);
  translate(0, -len);
  
  if (len > 4) {
    push();
    rotate(angle);
    branch(len * 0.67, depth - 1);
    pop();
    
    push();
    rotate(-angle);
    branch(len * 0.67, depth - 1);
    pop();
  }
}

Perlin Noise: Organic Randomness

Perlin noise creates smooth, natural-looking randomness:

function setup() {
  createCanvas(800, 800);
  background(0);
  noFill();
  stroke(255);
  
  const spacing = 20;
  const noiseScale = 0.01;
  
  for (let y = 0; y < height; y += spacing) {
    beginShape();
    for (let x = 0; x < width; x += 5) {
      const noiseVal = noise(x * noiseScale, y * noiseScale);
      const yOffset = map(noiseVal, 0, 1, -50, 50);
      vertex(x, y + yOffset);
    }
    endShape();
  }
}

3D Terrain Generation

let cols, rows;
let scl = 20;
let w = 2000;
let h = 1600;
let terrain = [];
let flying = 0;

function setup() {
  createCanvas(800, 800, WEBGL);
  cols = w / scl;
  rows = h / scl;
  
  for (let x = 0; x < cols; x++) {
    terrain[x] = [];
    for (let y = 0; y < rows; y++) {
      terrain[x][y] = 0;
    }
  }
}

function draw() {
  background(0);
  
  flying -= 0.05;
  let yoff = flying;
  
  for (let y = 0; y < rows; y++) {
    let xoff = 0;
    for (let x = 0; x < cols; x++) {
      terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      xoff += 0.1;
    }
    yoff += 0.1;
  }
  
  rotateX(PI / 3);
  translate(-w / 2, -h / 2);
  
  for (let y = 0; y < rows - 1; y++) {
    beginShape(TRIANGLE_STRIP);
    for (let x = 0; x < cols; x++) {
      const heightColor = map(terrain[x][y], -100, 100, 0, 255);
      fill(heightColor, 100, 255 - heightColor);
      
      vertex(x * scl, y * scl, terrain[x][y]);
      vertex(x * scl, (y + 1) * scl, terrain[x][y + 1]);
    }
    endShape();
  }
}

Circle Packing Algorithm

Create organic compositions by packing circles that don't overlap:

let circles = [];
let maxAttempts = 5000;
let minRadius = 5;
let maxRadius = 100;

function setup() {
  createCanvas(800, 800);
  background(20);
  colorMode(HSB, 360, 100, 100);
  
  // Try to add circles
  for (let i = 0; i < maxAttempts; i++) {
    const x = random(width);
    const y = random(height);
    const r = random(minRadius, maxRadius);
    
    let valid = true;
    
    // Check if overlaps with existing circles
    for (let circle of circles) {
      const d = dist(x, y, circle.x, circle.y);
      if (d < r + circle.r + 2) {
        valid = false;
        break;
      }
    }
    
    if (valid) {
      circles.push({ x, y, r, hue: random(360) });
    }
  }
  
  // Draw all circles
  noStroke();
  for (let circle of circles) {
    fill(circle.hue, 70, 90, 0.8);
    ellipse(circle.x, circle.y, circle.r * 2);
    
    // Inner circle
    fill(circle.hue, 50, 100, 0.6);
    ellipse(circle.x, circle.y, circle.r * 1.5);
  }
}

function mousePressed() {
  circles = [];
  setup();
}

Algorithmic Line Art

Create minimalist line-based compositions:

function setup() {
  createCanvas(800, 800);
  background(250);
  
  const lines = 100;
  const spacing = width / lines;
  
  stroke(0);
  strokeWeight(1);
  
  for (let i = 0; i < lines; i++) {
    const x1 = spacing * i;
    const y1 = 0;
    
    const noiseVal = noise(i * 0.1);
    const x2 = map(noiseVal, 0, 1, 0, width);
    const y2 = height;
    
    line(x1, y1, x2, y2);
  }
}

Geometric Line Patterns

function setup() {
  createCanvas(800, 800);
  background(0);
  stroke(255);
  strokeWeight(0.5);
  
  const layers = 20;
  const step = TWO_PI / 100;
  
  translate(width / 2, height / 2);
  
  for (let i = 0; i < layers; i++) {
    const radius = map(i, 0, layers, 50, 350);
    const rotation = map(i, 0, layers, 0, TWO_PI);
    
    beginShape();
    for (let angle = 0; angle < TWO_PI; angle += step) {
      const xoff = cos(angle + rotation) * radius;
      const yoff = sin(angle + rotation) * radius;
      
      const noiseVal = noise(xoff * 0.01, yoff * 0.01, i * 0.1);
      const r = radius + map(noiseVal, 0, 1, -50, 50);
      
      const x = cos(angle) * r;
      const y = sin(angle) * r;
      
      vertex(x, y);
    }
    endShape(CLOSE);
  }
}

Color Palettes

Create harmonious color schemes:

// Predefined palettes
const palettes = {
  sunset: ['#FF6B35', '#F7931E', '#FDC830', '#F37335'],
  ocean: ['#0077BE', '#00A8E8', '#00C9FF', '#40E0D0'],
  forest: ['#2C5F2D', '#97BC62', '#C0D890', '#E8F5C8'],
  neon: ['#FF006E', '#FB5607', '#FFBE0B', '#8338EC']
};

function setup() {
  createCanvas(800, 800);
  background(20);
  
  const palette = palettes.neon;
  const cols = 20;
  const rows = 20;
  const size = width / cols;
  
  noStroke();
  
  for (let i = 0; i < cols; i++) {
    for (let j = 0; j < rows; j++) {
      const x = i * size;
      const y = j * size;
      
      // Pick random color from palette
      const col = random(palette);
      fill(col);
      
      const shapeType = floor(random(3));
      
      if (shapeType === 0) {
        circle(x + size / 2, y + size / 2, size * 0.8);
      } else if (shapeType === 1) {
        rect(x + size * 0.1, y + size * 0.1, size * 0.8, size * 0.8);
      } else {
        triangle(
          x + size / 2, y + size * 0.1,
          x + size * 0.9, y + size * 0.9,
          x + size * 0.1, y + size * 0.9
        );
      }
    }
  }
}

Saving Your Artwork

Add export functionality:

function keyPressed() {
  if (key === 's' || key === 'S') {
    const timestamp = year() + nf(month(), 2) + nf(day(), 2) + 
                     '-' + nf(hour(), 2) + nf(minute(), 2) + nf(second(), 2);
    saveCanvas(`generative-art-${timestamp}`, 'png');
  }
  
  if (key === ' ') {
    // Regenerate art
    setup();
  }
}

Creating NFT-Ready Art

Generate high-resolution outputs:

function setup() {
  // High resolution for NFTs
  const dimension = 4000;
  createCanvas(dimension, dimension);
  pixelDensity(1);
  
  // Your generative code here
  generateArt();
  
  // Auto-save when done
  noLoop();
}

function generateArt() {
  background(0);
  
  // Create unique seed for reproducibility
  const seed = floor(random(1000000));
  randomSeed(seed);
  noiseSeed(seed);
  
  console.log('Artwork seed:', seed);
  
  // Your art generation logic
  // ...
}

Conclusion

Generative art opens infinite creative possibilities through code. We've explored:

  • Basic Patterns: Grids, randomness, color theory
  • Flow Fields: Particle systems following vector fields
  • Fractals: Recursive tree structures
  • Perlin Noise: Smooth, organic randomness
  • Circle Packing: Organic composition algorithms
  • Line Art: Minimalist geometric patterns
  • Color Palettes: Creating harmonious schemes

Key Takeaways

  • Start simple, add complexity gradually
  • Use randomness with intention
  • Perlin noise creates organic patterns
  • Recursion generates fractals
  • Save with high resolution for prints/NFTs
  • Document your random seeds for reproducibility

Next Steps

  1. Experiment with 3D shapes using WEBGL mode
  2. Add audio reactivity using p5.sound
  3. Create interactive installations
  4. Mint your art as NFTs
  5. Explore other frameworks like Processing, Three.js

The beauty of generative art is that small parameter changes create entirely new pieces. Keep experimenting!


This tutorial is part of the PlayHve Creative Coding series. Explore more techniques for digital art and interactive experiences.

Written by