function setup() {
createCanvas(windowWidth, windowHeight);
// Create dat.gui
gui = new dat.GUI();
gui.add(settings, 'particleSpeed', 1, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
).name('crazy meter');
for (let i = 0; i < 100; i++) {
particles.push(new Particle(random(width), random(height)));
}
}
Line-by-line explanation (5 lines)
đź”§ Subcomponents:
calculation
Canvas Setup
createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window
calculation
dat.gui Initialization
gui = new dat.GUI();
Creates a control panel that will hold interactive sliders
calculation
Crazy Meter Slider
gui.add(settings, 'particleSpeed', 1, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
).name('crazy meter');
Adds a slider to control particleSpeed between 1 and a huge number, labeled 'crazy meter'
for-loop
Create 100 Particles
for (let i = 0; i < 100; i++) {
particles.push(new Particle(random(width), random(height)));
}
Spawns 100 Particle objects at random positions across the canvas
createCanvas(windowWidth, windowHeight);
- Creates a canvas as wide and tall as the browser window, so the sketch fills the entire screen
gui = new dat.GUI();
- Initializes the dat.gui library, which creates an interactive control panel in the top-right corner
gui.add(settings, 'particleSpeed', 1, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
).name('crazy meter');
- Adds a slider to the gui panel that controls the 'particleSpeed' property in settings - you can drag the slider to change this number, and it updates in real-time
for (let i = 0; i < 100; i++) {
- Starts a loop that will run 100 times, creating 100 particles
particles.push(new Particle(random(width), random(height)));
- Creates a new Particle at a random x and y position, and adds it to the particles array
🔬 These four lines create the wrapping effect. What if you changed them to bounce the particle instead - for example, this.vel.x *= -1 when hitting an edge? How would the motion feel different?
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.y < 0) this.pos.y = height;
if (this.pos.y > height) this.pos.y = 0;
class Particle {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(random(-1, 1), random(-1, 1));
this.vel.setMag(settings.particleSpeed); // Use GUI speed for initial magnitude
this.size = random(3, 8);
this.color = color(random(150, 255), random(100, 200), random(200, 255), 150);
}
attract(tx, ty) {
let target = createVector(tx, ty);
let force = p5.Vector.sub(target, this.pos);
force.setMag(5); // Keep acceleration force, but limit max speed
this.vel.add(force);
this.vel.limit(settings.particleSpeed); // Use GUI speed as max speed limit
}
update() {
this.pos.add(this.vel);
// Wrap around canvas edges instead of bouncing
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.y < 0) this.pos.y = height;
if (this.pos.y > height) this.pos.y = 0;
}
display() {
noStroke();
fill(this.color);
ellipse(this.pos.x, this.pos.y, this.size);
}
}
Line-by-line explanation (19 lines)
đź”§ Subcomponents:
calculation
Particle Constructor
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector(random(-1, 1), random(-1, 1));
this.vel.setMag(settings.particleSpeed);
this.size = random(3, 8);
this.color = color(random(150, 255), random(100, 200), random(200, 255), 150);
}
Initializes a new particle with a position, random velocity direction, random size, and random pastel color
calculation
Attraction to Mouse
attract(tx, ty) {
let target = createVector(tx, ty);
let force = p5.Vector.sub(target, this.pos);
force.setMag(5);
this.vel.add(force);
this.vel.limit(settings.particleSpeed);
}
Calculates a force vector pointing from the particle toward the mouse and adds it to velocity, capping speed at the crazy meter value
calculation
Position Update and Wrapping
update() {
this.pos.add(this.vel);
if (this.pos.x < 0) this.pos.x = width;
if (this.pos.x > width) this.pos.x = 0;
if (this.pos.y < 0) this.pos.y = height;
if (this.pos.y > height) this.pos.y = 0;
}
Moves the particle by its velocity and wraps it to the opposite side of the canvas if it exits
calculation
Draw Particle
display() {
noStroke();
fill(this.color);
ellipse(this.pos.x, this.pos.y, this.size);
}
Draws the particle as a filled, strokeless ellipse at its current position
constructor(x, y) {
- Defines the constructor method that runs when a new Particle is created - it receives x and y position parameters
this.pos = createVector(x, y);
- Creates a p5.Vector to store the particle's position using the x and y parameters passed in
this.vel = createVector(random(-1, 1), random(-1, 1));
- Creates a p5.Vector with random x and y values between -1 and 1 - this will be the particle's velocity direction
this.vel.setMag(settings.particleSpeed);
- Uses setMag() to set the velocity vector's length (magnitude) to match the crazy meter slider value, so all particles move at the same speed initially
this.size = random(3, 8);
- Assigns a random size between 3 and 8 pixels to this particle, so they are not all identical
this.color = color(random(150, 255), random(100, 200), random(200, 255), 150);
- Creates a random pastel color by picking RGB values in the 100-255 range (avoiding very dark colors), with alpha 150 for semi-transparency
let target = createVector(tx, ty);
- Creates a vector representing the target position (the mouse cursor)
let force = p5.Vector.sub(target, this.pos);
- Subtracts the particle's position from the target position, creating a vector that points from the particle toward the mouse
force.setMag(5);
- Sets the magnitude of the force to a constant 5, so the pull strength is always the same regardless of distance from the mouse
this.vel.add(force);
- Adds the force vector to the particle's velocity, accelerating it toward the mouse
this.vel.limit(settings.particleSpeed);
- Caps the particle's velocity to the crazy meter value so it cannot go faster than that limit, preventing chaotic speeds
this.pos.add(this.vel);
- Adds the velocity vector to the position vector, moving the particle by one step in its current direction
if (this.pos.x < 0) this.pos.x = width;
- If the particle moves off the left edge of the canvas, teleport it to the right edge, creating a seamless wrap effect
if (this.pos.x > width) this.pos.x = 0;
- If the particle moves off the right edge, teleport it to the left edge
if (this.pos.y < 0) this.pos.y = height;
- If the particle moves off the top edge, teleport it to the bottom edge
if (this.pos.y > height) this.pos.y = 0;
- If the particle moves off the bottom edge, teleport it to the top edge
noStroke();
- Disables the outline of shapes, so the particles will be solid colored circles without borders
fill(this.color);
- Sets the fill color to this particle's unique pastel color
ellipse(this.pos.x, this.pos.y, this.size);
- Draws an ellipse (circle) at the particle's current position with its assigned size