Generative Art Algorithms
Create art through code, mathematics, and emergence.
Core Philosophy
Generative Art Principles
-
Rules create emergence - Simple rules yield complex results
-
Controlled randomness - Seeded random for reproducibility
-
Parameter exploration - Same algorithm, infinite variations
-
Happy accidents - Bugs as features
The Creative Coding Loop
Idea → Algorithm → Parameters → Render → Evaluate → Iterate ↑ │ └───────────────────────────────────────────────────┘
Noise Functions
Perlin/Simplex Noise
Smooth, continuous random values perfect for organic motion.
// p5.js example function draw() { for (let x = 0; x < width; x++) { for (let y = 0; y < height; y++) { let n = noise(x * 0.01, y * 0.01, frameCount * 0.01); stroke(n * 255); point(x, y); } } }
Noise Parameters
Parameter Effect
Scale (frequency) Zoom level of noise (smaller = more detail)
Octaves Layers of detail
Amplitude Height of values
Time offset Animate through noise space
Noise Applications
-
Terrain generation
-
Texture synthesis
-
Organic movement
-
Flow fields
-
Cloud/smoke effects
Flow Fields
Basic Flow Field
// Generate angle at each grid point from noise function setup() { createCanvas(800, 800);
let resolution = 20; let cols = width / resolution; let rows = height / resolution;
for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { let angle = noise(x * 0.1, y * 0.1) * TWO_PI * 2;
// Draw vector
push();
translate(x * resolution, y * resolution);
rotate(angle);
stroke(0);
line(0, 0, resolution * 0.8, 0);
pop();
}
} }
Particles in Flow Field
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(); }
follow(flowField, resolution) { let x = floor(this.pos.x / resolution); let y = floor(this.pos.y / resolution); let index = x + y * floor(width / resolution); let force = flowField[index]; this.applyForce(force); }
applyForce(force) { this.acc.add(force); }
update() { this.vel.add(this.acc); this.vel.limit(this.maxSpeed); this.prevPos = this.pos.copy(); this.pos.add(this.vel); this.acc.mult(0); }
edges() { if (this.pos.x > width) { this.pos.x = 0; this.prevPos.x = 0; } if (this.pos.x < 0) { this.pos.x = width; this.prevPos.x = width; } if (this.pos.y > width) { this.pos.y = 0; this.prevPos.y = 0; } if (this.pos.y < 0) { this.pos.y = height; this.prevPos.y = height; } }
show() { stroke(0, 10); strokeWeight(1); line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y); } }
L-Systems
Grammar Structure
Axiom: Starting string Rules: Replacement rules Angle: Turning angle Iterations: Recursion depth
Classic L-Systems
Fractal Tree:
Axiom: F Rules: F → FF+[+F-F-F]-[-F+F+F] Angle: 25°
Koch Snowflake:
Axiom: F Rules: F → F+F--F+F Angle: 60°
Sierpinski Triangle:
Axiom: F-G-G Rules: F → F-G+F+G-F, G → GG Angle: 120°
L-System Rendering
// Interpret string as drawing commands function render(sentence) { for (let char of sentence) { switch(char) { case 'F': line(0, 0, 0, -len); translate(0, -len); break; case '+': rotate(angle); break; case '-': rotate(-angle); break; case '[': push(); break; case ']': pop(); break; } } }
Fractals
Mandelbrot Set
function mandelbrot(x, y, maxIter) { let real = x; let imag = y;
for (let i = 0; i < maxIter; i++) { let tempReal = real * real - imag * imag + x; imag = 2 * real * imag + y; real = tempReal;
if (real * real + imag * imag > 4) {
return i;
}
} return maxIter; }
Julia Set
Same iteration, different starting point:
function julia(x, y, cx, cy, maxIter) { let real = x; let imag = y;
for (let i = 0; i < maxIter; i++) { let tempReal = real * real - imag * imag + cx; imag = 2 * real * imag + cy; real = tempReal;
if (real * real + imag * imag > 4) {
return i;
}
} return maxIter; }
Recursive Subdivision
function subdivide(x, y, w, h, depth) { if (depth === 0 || w < 2 || h < 2) { rect(x, y, w, h); return; }
let splitH = random() > 0.5;
if (splitH) { let split = random(0.3, 0.7) * w; subdivide(x, y, split, h, depth - 1); subdivide(x + split, y, w - split, h, depth - 1); } else { let split = random(0.3, 0.7) * h; subdivide(x, y, w, split, depth - 1); subdivide(x, y + split, w, h - split, depth - 1); } }
Particle Systems
Basic Particle
class Particle { constructor(x, y) { this.pos = createVector(x, y); this.vel = p5.Vector.random2D().mult(random(1, 3)); this.acc = createVector(0, 0); this.lifespan = 255; this.size = random(5, 15); }
applyForce(force) { this.acc.add(force); }
update() { this.vel.add(this.acc); this.pos.add(this.vel); this.acc.mult(0); this.lifespan -= 2; }
isDead() { return this.lifespan <= 0; }
show() { noStroke(); fill(255, this.lifespan); ellipse(this.pos.x, this.pos.y, this.size); } }
Forces
// Gravity let gravity = createVector(0, 0.1); particle.applyForce(gravity);
// Attraction to point function attract(target, particle, strength) { let force = p5.Vector.sub(target, particle.pos); let distance = constrain(force.mag(), 5, 25); force.normalize(); let magnitude = strength / (distance * distance); force.mult(magnitude); return force; }
// Repulsion function repel(target, particle, strength) { return attract(target, particle, -strength); }
Color Algorithms
Palette Generation
// Complementary function complementary(hue) { return [(hue + 180) % 360]; }
// Triadic function triadic(hue) { return [(hue + 120) % 360, (hue + 240) % 360]; }
// Analogous function analogous(hue, spread = 30) { return [(hue - spread + 360) % 360, (hue + spread) % 360]; }
// Split complementary function splitComplementary(hue) { return [(hue + 150) % 360, (hue + 210) % 360]; }
Color Interpolation
// Lerp between colors function lerpColor(c1, c2, t) { colorMode(HSB); return color( lerp(hue(c1), hue(c2), t), lerp(saturation(c1), saturation(c2), t), lerp(brightness(c1), brightness(c2), t) ); }
// Palette from noise function noiseColor(t, palette) { let n = noise(t) * (palette.length - 1); let i = floor(n); let f = n - i; return lerpColor(palette[i], palette[i + 1], f); }
Pattern Algorithms
Truchet Tiles
function truchetTile(x, y, size, type) { push(); translate(x, y);
if (type === 0) { arc(0, 0, size, size, 0, HALF_PI); arc(size, size, size, size, PI, PI + HALF_PI); } else { arc(size, 0, size, size, HALF_PI, PI); arc(0, size, size, size, PI + HALF_PI, TWO_PI); }
pop(); }
Voronoi
// Simple Voronoi via distance check function voronoi(points) { for (let x = 0; x < width; x++) { for (let y = 0; y < height; y++) { let closest = 0; let minDist = Infinity;
for (let i = 0; i < points.length; i++) {
let d = dist(x, y, points[i].x, points[i].y);
if (d < minDist) {
minDist = d;
closest = i;
}
}
stroke(points[closest].color);
point(x, y);
}
} }
Seeded Randomness
// Use seed for reproducibility let seed = 12345; randomSeed(seed); noiseSeed(seed);
// Or generate from string function hashCode(str) { let hash = 0; for (let i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash = hash & hash; } return hash; }
randomSeed(hashCode("my-artwork-2024"));
Creative Coding Tools
Tool Language Best For
p5.js JavaScript Beginners, web
Processing Java Desktop, print
Three.js JavaScript 3D, WebGL
TouchDesigner Visual Real-time, AV
Hydra JavaScript Live visuals
Shadertoy GLSL GPU shaders
Nannou Rust Performance
Related Skills
Complementary Skills (Use Together)
-
algorithmic-art - Complete workflow for creating interactive p5.js generative art
-
canvas-design - Canvas API patterns for custom rendering
-
theme-factory - Design color palettes and visual themes
Alternative Skills (Similar Purpose)
- three-js-interactive-builder - For 3D generative art with Three.js
Prerequisite Skills (Learn First)
- None required - includes foundational creative coding patterns
References
-
references/noise-recipes.md
-
Noise function patterns
-
references/color-palettes.md
-
Curated color schemes
-
references/shader-patterns.md
-
GLSL snippets