🔬 The rocket always goes straight up (x=0), while explosion particles go in random directions. What happens if you change the rocket's x from 0 to random(-2, 2)? How would that change the rocket's path?
if (this.firework) {
// Rocket particle goes straight up with random initial speed
this.vel = createVector(0, random(-10, -5));
} else {
// Explosion particles burst outwards with random velocity
this.vel = p5.Vector.random2D(); // Random direction
this.vel.mult(random(1, 6)); // Random speed
}
🔬 Explosion particles are drawn as small dots with fading alpha. What happens if you change strokeWeight(2) to strokeWeight(5)? Or if you change the saturation (the 255 after this.hu) to 100?
strokeWeight(2);
stroke(this.hu, 255, 255, this.lifespan);
class Particle {
constructor(x, y, hu, firework) {
this.pos = createVector(x, y); // Position
this.firework = firework; // Is this particle part of the initial rocket or the explosion?
this.lifespan = 255; // How long the particle lives (alpha value)
this.hu = hu; // Hue for its color
// Initial velocity
if (this.firework) {
// Rocket particle goes straight up with random initial speed
this.vel = createVector(0, random(-10, -5));
} else {
// Explosion particles burst outwards with random velocity
this.vel = p5.Vector.random2D(); // Random direction
this.vel.mult(random(1, 6)); // Random speed
}
this.acc = createVector(0, 0); // Acceleration
}
// Apply a force to the particle (e.g., gravity)
applyForce(force) {
this.acc.add(force);
}
// Update particle's position and lifespan
update() {
if (!this.firework) {
// Explosion particles slow down due to air resistance (or just simple drag)
this.vel.mult(0.9);
this.lifespan -= 5; // Fade out
}
this.vel.add(this.acc); // Add acceleration to velocity
this.pos.add(this.vel); // Add velocity to position
this.acc.mult(0); // Reset acceleration for the next frame
}
// Check if the particle has died (lifespan is 0)
done() {
return this.lifespan < 0;
}
// Display the particle
show() {
colorMode(HSB, 255); // Ensure HSB mode for particle colors
if (!this.firework) {
// Explosion particles are smaller and fade out
strokeWeight(2);
stroke(this.hu, 255, 255, this.lifespan); // Color with fading alpha
} else {
// Rocket particle is brighter and has a tail
strokeWeight(4);
stroke(this.hu, 255, 255); // Bright color
}
point(this.pos.x, this.pos.y); // Draw a point at the particle's position
}
}
Line-by-line explanation (18 lines)
🔧 Subcomponents:
calculation
Particle Constructor
constructor(x, y, hu, firework) {
Initializes a new Particle with position, hue, and velocity depending on whether it is a rocket or explosion particle
conditional
Rocket vs Explosion Velocity
if (this.firework) {
Rocket particles launch straight upward; explosion particles burst outward in random directions
calculation
Force Application
this.acc.add(force);
Accumulates forces (like gravity) by adding them to the acceleration vector
calculation
Physics Integration
this.vel.add(this.acc);
Applies acceleration to velocity, then velocity to position—the core physics loop
conditional
Explosion Particle Decay
if (!this.firework) {
Only explosion particles fade and slow down; rocket particles maintain brightness and speed until explosion
constructor(x, y, hu, firework) {
- The constructor function runs when a new Particle is created. It receives x and y starting coordinates, hu (hue), and a boolean firework flag indicating whether this is a rocket or explosion particle
this.pos = createVector(x, y);
- Stores the particle's starting position as a p5.Vector, which we will update every frame
this.firework = firework;
- Saves the boolean flag so we can check later whether this is a rocket particle or an explosion particle—they behave differently
this.lifespan = 255;
- Sets the particle's initial lifespan to 255. This value decreases over time and controls the alpha (transparency), so the particle gradually fades out
this.hu = hu;
- Stores the hue value (0-255) so all particles in one explosion share the same color
this.vel = createVector(0, random(-10, -5));
- Rocket particles start with zero horizontal velocity and a negative vertical velocity between -10 and -5, so they shoot straight upward at varying speeds
this.vel = p5.Vector.random2D();
- Creates a random 2D unit vector, giving the explosion particle a random direction
this.vel.mult(random(1, 6));
- Multiplies the velocity by a random number between 1 and 6, making each explosion particle burst outward at a different speed
this.acc = createVector(0, 0);
- Initializes acceleration to zero. Acceleration will be updated each frame when gravity is applied via applyForce()
this.acc.add(force);
- Adds a force vector (like gravity) to the acceleration, accumulating multiple forces if needed
this.vel.mult(0.9);
- Multiplies explosion particle velocity by 0.9 each frame, simulating air resistance and drag so particles slow down as they float
this.lifespan -= 5;
- Decreases the lifespan by 5 each frame, causing explosion particles to fade out—when lifespan reaches 0, the particle is removed
this.vel.add(this.acc);
- Adds acceleration to velocity—this is how forces like gravity change the particle's speed
this.pos.add(this.vel);
- Adds velocity to position, moving the particle each frame. This simple loop (force → acceleration → velocity → position) is the foundation of physics simulation
this.acc.mult(0);
- Resets acceleration to zero at the end of the frame. Forces must be reapplied every frame—they do not persist automatically
return this.lifespan < 0;
- Returns true if the particle's lifespan is below zero, meaning it has completely faded and should be removed from the simulation
stroke(this.hu, 255, 255, this.lifespan);
- Sets the stroke color using HSB mode: hue is this.hu (the particle's assigned color), saturation is 255 (fully vibrant), brightness is 255 (fully bright), and alpha is this.lifespan (fades as lifespan decreases)
point(this.pos.x, this.pos.y);
- Draws a single pixel at the particle's current position with the stroke color set above
🔬 The rocket explodes when its upward velocity becomes zero or positive (>= 0), meaning it has peaked. What happens if you change >= 0 to >= -3? When would the explosion happen earlier or later?
// If the rocket has reached its peak (velocity in Y direction becomes positive), explode!
if (this.firework.vel.y >= 0) {
this.exploded = true;
this.explode();
}
🔬 After explosion, this loop updates all particles and removes dead ones. What happens if you comment out the applyForce(gravity) line? How would particles behave without gravity pulling them down?
} else {
// If exploded, update all explosion particles
for (let i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].applyForce(gravity); // Apply gravity to particles
this.particles[i].update();
// Remove dead particles from the array
if (this.particles[i].done()) {
this.particles.splice(i, 1);
}
}
}
class Firework {
constructor(hu) {
this.hu = hu; // Hue for this firework
this.firework = new Particle(random(width), height, this.hu, true); // The rocket particle
this.exploded = false; // Has the rocket exploded yet?
this.particles = []; // Array to hold explosion particles
}
// Check if the firework (and its explosion) is done
done() {
return this.exploded && this.particles.length === 0;
}
// Update the firework's state
update() {
if (!this.exploded) {
this.firework.applyForce(gravity); // Apply gravity to the rocket
this.firework.update(); // Update rocket position
// If the rocket has reached its peak (velocity in Y direction becomes positive), explode!
if (this.firework.vel.y >= 0) {
this.exploded = true;
this.explode();
}
} else {
// If exploded, update all explosion particles
for (let i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].applyForce(gravity); // Apply gravity to particles
this.particles[i].update();
// Remove dead particles from the array
if (this.particles[i].done()) {
this.particles.splice(i, 1);
}
}
}
}
// Create explosion particles
explode() {
// Generate a random number of particles
for (let i = 0; i < random(50, 150); i++) {
// Each particle starts at the rocket's position
this.particles.push(new Particle(this.firework.pos.x, this.firework.pos.y, this.hu, false));
}
}
// Display the firework
show() {
if (!this.exploded) {
// Display the rocket particle
this.firework.show();
} else {
// Display all explosion particles
for (let particle of this.particles) {
particle.show();
}
}
}
}
Line-by-line explanation (22 lines)
🔧 Subcomponents:
calculation
Firework Constructor
constructor(hu) {
Creates a new firework with a random hue and initializes a rocket particle
calculation
Rocket Particle Creation
this.firework = new Particle(random(width), height, this.hu, true);
Creates the initial rocket particle at a random horizontal position at the bottom of the canvas
conditional
Explosion Trigger
if (this.firework.vel.y >= 0) {
Detects when the rocket has reached its peak (velocity stops being negative) and triggers the explosion
for-loop
Particle Burst Loop
for (let i = 0; i < random(50, 150); i++) {
Creates 50-150 explosion particles at the rocket's final position, all with the firework's hue
for-loop
Explosion Particles Update
for (let i = this.particles.length - 1; i >= 0; i--) {
Updates all explosion particles each frame and removes the ones that have fully faded
constructor(hu) {
- The constructor receives a hue value and initializes a new Firework object with that color
this.hu = hu;
- Stores the hue so all explosion particles will inherit the rocket's color
this.firework = new Particle(random(width), height, this.hu, true);
- Creates a new Particle at a random horizontal position (random(width)) at the bottom of the canvas (height), passes the hue, and sets the firework flag to true so it launches upward
this.exploded = false;
- Tracks whether the rocket has exploded yet. Starts as false and becomes true when the rocket reaches its peak
this.particles = [];
- Initializes an empty array that will hold all the explosion particles once the rocket explodes
return this.exploded && this.particles.length === 0;
- Returns true only if the firework has exploded AND all its explosion particles have faded and been removed from the array—this signals it is safe to delete the Firework object
this.firework.applyForce(gravity);
- Applies the global gravity force to the rocket particle, pulling it downward and slowing its upward motion
this.firework.update();
- Updates the rocket particle's velocity and position each frame, applying the gravity force
if (this.firework.vel.y >= 0) {
- Checks whether the rocket's vertical velocity has stopped being negative (upward) and has become zero or positive (starting to fall). This is the peak, the moment to explode
this.exploded = true;
- Sets the exploded flag to true, switching the firework from rocket mode to explosion mode
this.explode();
- Calls the explode() method to create all the explosion particles at the rocket's current position
for (let i = this.particles.length - 1; i >= 0; i--) {
- Loops backward through the explosion particles array, updating and removing dead particles (same backward-loop pattern used in draw())
this.particles[i].applyForce(gravity);
- Applies gravity to each explosion particle so they fall back down while fading out
this.particles[i].update();
- Updates each explosion particle's position, velocity, acceleration, and lifespan
if (this.particles[i].done()) {
- Checks if this explosion particle's lifespan has dropped below zero (fully faded)
this.particles.splice(i, 1);
- Removes the dead particle from the array, freeing memory
for (let i = 0; i < random(50, 150); i++) {
- Generates a random number between 50 and 150, so each firework explosion has a slightly different number of sparks
this.particles.push(new Particle(this.firework.pos.x, this.firework.pos.y, this.hu, false));
- Creates a new Particle at the rocket's explosion position, assigns it the firework's hue, and sets the firework flag to false so it bursts outward rather than rising
if (!this.exploded) {
- If the rocket hasn't exploded yet, display the rocket particle
this.firework.show();
- Draws the rocket particle to the canvas
for (let particle of this.particles) {
- If the rocket has exploded, loop through all explosion particles using a for-of loop (a cleaner way than indexed for-loops for simple iteration)
particle.show();
- Draws each explosion particle to the canvas