Inspiration
This code is inspired by a video from “The Coding Train” channel on YouTube. The video shows how to create a flow field simulation using particles that follow the flow of a vector field. It uses Object-Oriented Programming, arrays, and Perlin noise to create flow field particles that look very aesthetically pleasing.
Concept
The concept behind the flow field simulation is that particles move according to a vector field, which defines the direction and magnitude of the flow. The vector field is generated using noise, so that the flow is not predictable and creates a natural, organic look. The particles follow the flow field by calculating the angle of the vector at their current position, and adjusting their velocity and position accordingly.
Implementation
The implementation of the code involves creating a Particle class to represent each particle in the system. This class contains a number of methods to control the particle’s motion. The update()
method updates the velocity and position of the particle based on its acceleration. The follow()
method uses the particle’s current position to find the corresponding vector in the flow field and apply it as a force to the particle. The display()
method draws a line from the particle’s current position to its previous position, and the updatePrev()
method updates the previous position to be the current position. The resetParticles()
method checks if the particle has gone out of bounds and wraps it back to the other side of the canvas if it has.
The main setup function generates the flow field and creates the particles. The flow field is generated by dividing the canvas into a grid of cells, and then generating a random vector for each cell. The number of columns and rows in the grid is calculated based on the size of the canvas and the scale factor, s
. The setup()
function also creates the particles by creating new instances of the Particle class and adding them to the particles
array.
The draw()
function updates the flow field and the particles. First, it updates the noise offset to create a smoothly changing flow field. Then, it loops through the grid and calculates a new angle for each cell based on the noise function. This angle is converted into a vector, which is stored in the flowfield
array. Next, the function loops through the particles, and for each particle, it calls the follow()
method to apply the corresponding flow field vector to the particle. Then, the update()
method is called to update the particle’s velocity and position based on its acceleration. Finally, the display()
method is called to draw the particle and the resetParticles()
method is called to wrap the particle back to the other side of the canvas if it has gone out of bounds.
// Class for a particle that moves based on a perlin noise flow field class Particle { constructor() { // Initial position of the particle at a random point on the canvas this.pos = createVector(random(width), random(height)); // Initial velocity set to (0, 0) this.vel = createVector(0, 0); // Initial acceleration set to (0, 0) this.accel = createVector(0, 0); // Maximum speed that the particle can move this.maxV = 4; // Store the previous position of the particle this.prevPos = this.pos.copy(); } // Follow the flow field vectors follow(vectors) { // Calculate the grid position of the particle let x = floor(this.pos.x / s); let y = floor(this.pos.y / s); let index = x + y * cols; // Get the force vector at that grid position let force = vectors[index]; // Apply a force to the particle's acceleration this.accel.add(force); } // Update the position of the particle based on its velocity and acceleration update() { // Add the acceleration to the velocity this.vel.add(this.accel); // Limit the velocity to the maxV this.vel.limit(this.maxV); // Add the velocity to the position this.pos.add(this.vel); // Reset the acceleration to (0, 0) this.accel.mult(0); } // Display the particle as a line connecting its current and previous position display() { stroke(220); strokeWeight(1); line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y); this.updatePrev(); } // Update the previous position of the particle updatePrev() { this.prevPos.x = this.pos.x; this.prevPos.y = this.pos.y; } // Check if the particle has gone outside the canvas and wrap it around if necessary particleReset() { 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(); } } }
Challenges
One of the challenges in this project was figuring out how to create a smooth flow field that changes over time. This was achieved by using the noise function to generate random values for each cell in the grid. By updating the noise offset in the draw()
function, the flow field smoothly changes over time, creating a dynamic and interesting visual.
Another challenge was figuring out how to apply the flow field vectors to the particles in a way that creates a believable and visually appealing motion. This was achieved by dividing the canvas into a grid of cells and using the particle’s position to find the corresponding flow field vector. The particle’s velocity is then updated based on this vector, creating a believable motion that follows the flow of the flow field.
Reflection
This project was a fun and interesting exploration of particle systems and flow fields. By creating a simple system of particles that follow a flow field, it is possible to create a wide variety of dynamic and visually appealing patterns. The use of the noise function to generate the flow field allows for a high degree of control over the motion of the particles and the overall visual appearance of the system. This project was a valuable learning experience, and it has given me a deeper appreciation for the potential of particle systems and flow fields for creating dynamic, organic and engaging visuals using object-oriented programming, arrays, and Perlin noise.
Reference
Sketch Link: https://editor.p5js.org/swostikpati/full/-KXM2Ag3s