Approach and Concept:
Starting off this project, I realized I could potentially look to screensavers of different kinds for motivating my generative art piece. Upon googling, I was instantly reminded of the iconic Mac screensaver – ‘Drift’.
Attempting to recreate the piece proved incredibly hard as it seems to use some sort of fluid-based physics simulation rather than simple noise. Additionally, the particles itself were 3D and detailed with shadows, depth etc. I gave up on the idea for a bit and decided to create a cool ‘background’ first. I did this with an idea I got during my previous project -using Perlin noise on each pixel to evolve the color based on the x,y, and time axes to make an evolving gradient.
However, this gave me a new idea – what if every pixel was a particle that moved on a trail decided by noise? That could certainly simulate the effect I wanted to an extent! With some code changes, and a lot more tinkering with variables – I finally came up with my final design:
Bonus: Click to see the animation speed change, and hover to see how the particles interact with the mouse!!
Highlights:
My highlight for this project is modeling the interplay between the noise evolution of every particle’s size,angle, and color. Moreover, I added some user interaction elements by adding an effect when we hover a mouse over the animation: the particles get repelled!
I had to bake in some considerations here so I only made this effect available away from the edges of the screen – so that the user can see the art without the mouse effect easily. I was also worried about the effect strength since the result wasn’t smooth and didn’t go with the aesthetics of my vision originally, but the normalize function came into the rescue here!
update() {
let r = noise(this.pos.x * noiseScale, this.pos.y * noiseScale, timeC) * 255;
let g = noise(this.pos.x * noiseScale + 100, this.pos.y * noiseScale + 100, timeC) * 255;
let b = noise(this.pos.x * noiseScale + 200, this.pos.y * noiseScale + 200, timeC) * 255;
let alpha = noise(this.pos.x * noiseScale + 300, this.pos.y * noiseScale + 300, timeC) * 150 + 105;
this.color = color(r, g, b, alpha);
this.angle = noise(this.pos.x * 0.01, this.pos.y * 0.01, timeMove) * TWO_PI;
let move = p5.Vector.fromAngle(this.angle);
move.mult(currSpeed); //changes the speed based on mouse click
this.pos.add(move);
let sizeNoise = noise(this.pos.x * noiseScale, this.pos.y * noiseScale, timeMove*2);
this.size = sizeNoise*20; // Change these values to adjust the range of size based on noise
}
show() {
push();
translate(this.pos.x, this.pos.y);
rotate(this.angle);
fill(this.color);
rect(0, 0, this.size, this.size * 0.3); // making the rectangle elongated like a bristle
pop();
}
repulse(point) {
let dir = p5.Vector.sub(this.pos, point);
let dist = dir.mag();
if (dist < 100) { // Only push away if within 100 pixels
dir.normalize();
dir.mult(3); // Adjust for stronger or weaker repulsion
this.pos.add(dir);
}
}
Reflections and Future Work:
My initial goal was to make the particles 3-dimensional. When I actually attempted to do so, and mess with movement in the Z dimension, I ran into several issues both on the aesthetical and technical front. For example, using 3D graphics benefits immensely from the use of shaders – which are very different from the 2D objects we use to both learn and implement. Moreover, it required more geometrical calculations that seemed beyond the scope of the project.
If I had more time I would also try to see what variations I can get with using differently shaped particles, different types of noises etc.
Resources:
I realized at a later stage that this video does what I attempt to do, and took reference towards the last part of my coding:
https://youtu.be/BjoM9oKOAKY?si=tIbnzH-ndMvgCB2R
Another resource I used to understand the concept of vectors in p5.js:
https://www.youtube.com/watch?v=bKEaK7WNLzM&pp=ygUUY29kaW5nIHRyYWluIHZlY3RvcnM%3D
(Also, some other videos in the playlist)