Article summary
“Creativity itself doesn’t care at all about results — the only thing it craves is the process.” — Elizabeth Gilbert
Creating generative art is a tango between the rough boundaries you’ve established and the decisions of the algorithm. At the onset, I rarely have a rigid plan for what I’m going to make. I may know my starting point, but often the ending is a mystery.
In my previous post on generative art, I talked about my creative motivations and the theory of flow fields.
In this post, I’ll shed light on my creative process and illustrate the power of incremental change. I’ll showcase how an algorithm evolves as you feed its impulses, producing lovely abstract art.
Making a Grid
Building off the principles I learned from creating flow fields, I started by creating a grid for my canvas. This helps me keep an easy frame of reference as to where points will be placed in the coordinate system.
First I split the canvas into an evenly spaced-out grid, drawing a dot in each cell.
Surprisingly this is one of my favorite pieces I’ve made. I wasn’t expecting it to be an optical illusion and so difficult to look at which is cool.
This is what the code looks like:
let points = [];
let density = 25;
function setup() {
createCanvas(windowWidth, windowHeight);
background(30);
let space = width / density;
for (let x = 0; x < width; x += space) {
for (let y = 0; y < height; y += space) {
let p = createVector(x, y);
points.push(p);
}
}
}
function draw() {
noStroke();
for (let i = 0; i < 1000; i++) {
let angle = createVector(points[i].x, points[i].y);
points[i].add(angle);
stroke('white')
ellipse(points[i].x, points[i].y, 10);
}
}
In setup(), I create the aforementioned grid and iteratively step through it creating a vector that we push onto our array of points.
Then in draw(), I iterate through each of our points drawing an ellipse.
Adding Chaos to the Algorithm
From here I thought to myself, "It’s time to add some chaos to the mix."
More often than not when introducing randomness to your algorithms, you’ll want to use randomGaussian(). This allows you to use Gaussian distribution, so the numbers will be random but still slightly correlated.
For our ellipses, we originally used a width and height of 10. Instead, we are going to use the randomGuassian function to modulate the size of each ellipse.
// In setup
let p = createVector(x + randomGaussian(-10, 10), y + randomGaussian(-8, 8));
// In draw
ellipse(points[i].x, points[i].y, randomGaussian(40,25), randomGaussian(40, 10));
Looks cooler already, let’s see what happens when we only draw the outlines of each ellipse.
This also turned out to be pretty cool looking but not really the direction I was looking for. So, I quickly reverted my changes and started playing with color.
Playing with Color
In order to control the randomization of color, I prefer using HSB (Hue, Saturation, Brightness) color mode provided by the p5 library.
By keeping the saturation and brightness levels relatively consistent, I am able to simply increment the hue within a controlled range. This allows you to use random colors that aren’t wildly different from each other, creating a much more pleasing aesthetic.
Final Thoughts
With this post, I wanted to showcase how an algorithm can grow, or, evolve as you dance. With patience and a curious mind, a simple idea can snowball into beautiful creations.
When creating generative art, I find it important to relinquish some control and follow wherever the algorithm takes you.