Click to Launch Fireworks - xelsed.ai

This interactive sketch creates a fireworks display where clicking anywhere on the canvas launches a firework that travels to that point and explodes into 50-100 colorful particles. The particles fall with gravity and fade out over time, creating a realistic fireworks effect against a dark night sky using HSB color mode for vibrant colors.

๐ŸŽ“ Concepts You'll Learn

Object-oriented programming with classesArrays and dynamic removalVector mathematicsGravity and physics simulationColor modes (HSB)Event handling (mousePressed)Animation and frame-based updatesParticle systemsResponsive canvas sizing

๐Ÿ”„ Code Flow

Code flow showing setup, draw, mousepressed, windowresized, firework, particle

๐Ÿ’ก Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> backgroundfade[background-fade] draw --> fireworkloop[firework-loop] fireworkloop --> fireworkupdateshow[firework-update-show] fireworkupdateshow --> fireworkremoval[firework-removal] fireworkremoval --> draw fireworkloop --> fireworkconstructor[firework-constructor] fireworkconstructor --> fireworkdone[firework-done] fireworkdone --> fireworkupdate[firework-update] fireworkupdate --> fireworkshow[firework-show] fireworkshow --> fireworkexplode[firework-explode] fireworkexplode --> draw click setup href "#fn-setup" click draw href "#fn-draw" click backgroundfade href "#sub-background-fade" click fireworkloop href "#sub-firework-loop" click fireworkupdateshow href "#sub-firework-update-show" click fireworkremoval href "#sub-firework-removal" click fireworkconstructor href "#sub-firework-constructor" click fireworkdone href "#sub-firework-done" click fireworkupdate href "#sub-firework-update" click fireworkshow href "#sub-firework-show" click fireworkexplode href "#sub-firework-explode"

๐Ÿ“ Code Breakdown

setup()

setup() runs once when the sketch starts. Here we initialize the canvas, set visual properties, and create the gravity vector that will affect all particles throughout the sketch's lifetime.

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(0);
  gravity = createVector(0, 0.2);
  strokeWeight(4);
  colorMode(HSB, 255);
}

Line by Line:

createCanvas(windowWidth, windowHeight)
Creates a canvas that fills the entire browser window, making the sketch responsive to window size
background(0)
Sets the initial background to black (0 in HSB mode), creating the night sky effect
gravity = createVector(0, 0.2)
Creates a gravity vector pointing downward with a strength of 0.2. This will be applied to particles to make them fall
strokeWeight(4)
Sets the stroke weight to 4 pixels, making the firework points visible before explosion
colorMode(HSB, 255)
Switches from RGB to HSB color mode with values 0-255. HSB makes it easier to generate random vibrant colors by varying hue while keeping saturation and brightness high

draw()

draw() runs continuously at 60 frames per second. Each frame, we update all active fireworks, display them, and clean up any that have finished. The semi-transparent background creates the visual trail effect that makes fireworks look smooth and natural.

function draw() {
  background(0, 25);
  for (let i = fireworks.length - 1; i >= 0; i--) {
    let firework = fireworks[i];
    firework.update();
    firework.show();
    if (firework.done()) {
      fireworks.splice(i, 1);
    }
  }
}

๐Ÿ”ง Subcomponents:

calculation Background with Transparency background(0, 25)

Redraws the background with low alpha (25) to create a fading trail effect instead of completely clearing the canvas each frame

for-loop Reverse Loop Through Fireworks for (let i = fireworks.length - 1; i >= 0; i--)

Iterates backwards through the fireworks array, allowing safe removal of completed fireworks during iteration

calculation Update and Display Firework firework.update(); firework.show();

Updates the firework's position and state, then draws it on the canvas

conditional Remove Completed Fireworks if (firework.done()) { fireworks.splice(i, 1); }

Checks if a firework has completely faded out and removes it from the array to free memory

Line by Line:

background(0, 25)
Redraws a semi-transparent black background. The alpha value of 25 means old pixels fade slowly, creating trails behind the fireworks instead of erasing them instantly
for (let i = fireworks.length - 1; i >= 0; i--)
Loops backwards through the fireworks array. Backwards iteration is crucial because we're removing items during the loop - removing from the end doesn't affect indices we haven't reached yet
let firework = fireworks[i]
Gets the current firework object from the array for easier access
firework.update()
Calls the firework's update method, which moves it toward its target or updates its particles if it has exploded
firework.show()
Calls the firework's show method, which draws it as a point (before explosion) or draws all its particles (after explosion)
if (firework.done())
Checks if the firework has completely finished (all particles have faded out)
fireworks.splice(i, 1)
Removes the completed firework from the array, freeing up memory since it's no longer needed

mousePressed()

mousePressed() is a p5.js event function that automatically runs whenever the user clicks the mouse. By creating a new firework at the click location, we enable interactive control of the sketch.

function mousePressed() {
  fireworks.push(new Firework(mouseX, mouseY));
}

Line by Line:

fireworks.push(new Firework(mouseX, mouseY))
Creates a new Firework object at the current mouse position and adds it to the fireworks array. The firework will launch from the bottom of the canvas toward this clicked point

windowResized()

windowResized() is a p5.js event function that runs whenever the browser window is resized. This ensures the sketch always fills the entire window, regardless of how the user resizes their browser.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

Line by Line:

resizeCanvas(windowWidth, windowHeight)
Automatically resizes the canvas to match the new window dimensions when the browser window is resized, keeping the sketch responsive

class Firework

The Firework class manages the entire lifecycle of a firework: launching from the bottom toward a target, detecting when to explode, and managing all particles created by the explosion. It demonstrates object-oriented design by encapsulating all firework-related data and behavior.

class Firework {
  constructor(targetX, targetY) {
    this.pos = createVector(random(width), height);
    this.target = createVector(targetX, targetY);
    this.vel = p5.Vector.sub(this.target, this.pos);
    this.vel.setMag(random(8, 12));
    this.acc = createVector(0, 0);
    this.exploded = false;
    this.particles = [];
    this.hu = random(255);
  }

  done() {
    if (this.exploded) {
      return this.particles.length === 0;
    } else {
      return false;
    }
  }

  update() {
    if (!this.exploded) {
      this.vel.add(this.acc);
      this.pos.add(this.vel);
      this.acc.mult(0);
      let d = p5.Vector.dist(this.pos, this.target);
      if (d < 10) {
        this.explode();
        this.exploded = true;
      }
    } else {
      for (let i = this.particles.length - 1; i >= 0; i--) {
        let particle = this.particles[i];
        particle.update();
        if (particle.done()) {
          this.particles.splice(i, 1);
        }
      }
    }
  }

  show() {
    if (!this.exploded) {
      stroke(this.hu, 255, 255);
      point(this.pos.x, this.pos.y);
    } else {
      for (let particle of this.particles) {
        particle.show();
      }
    }
  }

  explode() {
    for (let i = 0; i < random(50, 100); i++) {
      this.particles.push(new Particle(this.pos.x, this.pos.y, this.hu));
    }
  }
}

๐Ÿ”ง Subcomponents:

calculation Firework Constructor constructor(targetX, targetY) { ... }

Initializes a new firework with starting position at bottom of canvas, target position at click location, and calculated velocity to reach the target

conditional Done Method done() { ... }

Checks if the firework has completely finished by verifying all particles have faded out

conditional Update Method update() { ... }

Moves the firework toward its target before explosion, or updates all particles after explosion

conditional Show Method show() { ... }

Draws the firework as a bright point before explosion, or draws all particles after explosion

for-loop Explode Method explode() { ... }

Creates 50-100 particles at the explosion point, spreading them outward with random velocities

Line by Line:

this.pos = createVector(random(width), height)
Sets the starting position at the bottom of the canvas (height) with a random X coordinate, so fireworks launch from different positions
this.target = createVector(targetX, targetY)
Stores the target position where the user clicked, which the firework will travel toward
this.vel = p5.Vector.sub(this.target, this.pos)
Calculates the velocity vector by subtracting the starting position from the target, creating a vector pointing toward the target
this.vel.setMag(random(8, 12))
Sets the speed of the firework's ascent to a random value between 8 and 12 pixels per frame, making each firework travel at a slightly different speed
this.exploded = false
Flag that tracks whether the firework has exploded yet. Used to determine whether to update position or update particles
this.particles = []
Empty array that will hold all particle objects created when the firework explodes
this.hu = random(255)
Assigns a random hue value (0-255) to this firework, which will be inherited by all its particles for consistent coloring
if (this.exploded) { return this.particles.length === 0 }
If exploded, the firework is done only when all particles have faded out (array is empty)
this.vel.add(this.acc)
Applies acceleration to velocity (currently 0 for fireworks, but structure allows for future modifications)
this.pos.add(this.vel)
Updates the firework's position by adding its velocity, moving it one step closer to the target
this.acc.mult(0)
Resets acceleration to zero for the next frame, following the physics simulation pattern
let d = p5.Vector.dist(this.pos, this.target)
Calculates the distance between the firework's current position and its target using vector distance
if (d < 10)
Checks if the firework is close enough to the target (within 10 pixels) to trigger explosion
particle.update()
Updates each particle's position and applies gravity to it
if (particle.done())
Checks if a particle has completely faded out (lifespan < 0)
this.particles.splice(i, 1)
Removes the faded particle from the array to free memory
stroke(this.hu, 255, 255)
Sets the stroke color to the firework's hue with full saturation and brightness, creating a bright colored point
point(this.pos.x, this.pos.y)
Draws a single point at the firework's current position before explosion
for (let particle of this.particles)
Loops through all particles after explosion and calls their show() method to draw them
for (let i = 0; i < random(50, 100); i++)
Creates a random number of particles between 50 and 100, adding variety to each explosion
this.particles.push(new Particle(this.pos.x, this.pos.y, this.hu))
Creates a new Particle at the explosion position with the firework's hue, and adds it to the particles array

class Particle

The Particle class represents individual particles created during a firework explosion. Each particle follows physics simulation: gravity affects acceleration, acceleration affects velocity, and velocity affects position. The lifespan property creates the fade effect by controlling alpha transparency, making particles gradually disappear.

class Particle {
  constructor(x, y, hu) {
    this.pos = createVector(x, y);
    this.vel = p5.Vector.random2D();
    this.vel.mult(random(1, 8));
    this.acc = createVector(0, 0);
    this.lifespan = 255;
    this.hu = hu;
  }

  done() {
    return this.lifespan < 0;
  }

  update() {
    this.acc.add(gravity);
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    this.acc.mult(0);
    this.lifespan -= 5;
  }

  show() {
    stroke(this.hu, 255, 255, this.lifespan);
    strokeWeight(2);
    point(this.pos.x, this.pos.y);
  }
}

๐Ÿ”ง Subcomponents:

calculation Particle Constructor constructor(x, y, hu) { ... }

Initializes a particle at the explosion point with random velocity spreading outward and inherited hue from the firework

conditional Done Method done() { return this.lifespan < 0 }

Checks if the particle has completely faded out

calculation Update Method update() { ... }

Applies gravity, updates velocity and position, and decreases lifespan to fade the particle

calculation Show Method show() { ... }

Draws the particle as a point with alpha based on remaining lifespan, creating the fade effect

Line by Line:

this.pos = createVector(x, y)
Sets the particle's starting position at the explosion point (center of the firework)
this.vel = p5.Vector.random2D()
Creates a random 2D vector with magnitude 1, pointing in a random direction for spreading particles outward
this.vel.mult(random(1, 8))
Multiplies the velocity by a random value between 1 and 8, giving particles different speeds so they spread at varying rates
this.acc = createVector(0, 0)
Initializes acceleration to zero, ready to receive gravity in the update method
this.lifespan = 255
Sets the particle's lifespan to 255 (fully opaque in HSB mode). This decreases each frame to create the fade effect
this.hu = hu
Stores the hue inherited from the parent firework, ensuring all particles in an explosion share the same color
return this.lifespan < 0
Returns true when lifespan becomes negative, indicating the particle is completely faded and should be removed
this.acc.add(gravity)
Adds the global gravity vector to the particle's acceleration, making it fall downward each frame
this.vel.add(this.acc)
Applies acceleration to velocity, increasing downward velocity due to gravity
this.pos.add(this.vel)
Updates the particle's position by adding velocity, moving it one step in its current direction
this.acc.mult(0)
Resets acceleration to zero for the next frame, following standard physics simulation practice
this.lifespan -= 5
Decreases lifespan by 5 each frame, causing the particle to fade out over approximately 51 frames (255 / 5)
stroke(this.hu, 255, 255, this.lifespan)
Sets the stroke color using the particle's hue with full saturation and brightness, but with alpha equal to lifespan so it fades as lifespan decreases
strokeWeight(2)
Sets stroke weight to 2 pixels, making particles slightly thinner than the initial firework point
point(this.pos.x, this.pos.y)
Draws a single point at the particle's current position

๐Ÿ“ฆ Key Variables

fireworks array

Stores all active Firework objects currently being animated. New fireworks are added when the user clicks, and removed when they finish exploding and fading

let fireworks = [];
gravity p5.Vector

A global vector representing gravitational acceleration (0, 0.2) that is applied to all particles, making them fall downward over time

let gravity = createVector(0, 0.2);
pos p5.Vector

Stores the current position of a firework or particle on the canvas (x, y coordinates)

this.pos = createVector(random(width), height);
target p5.Vector

Stores the target position where the firework should travel to (the point where the user clicked)

this.target = createVector(targetX, targetY);
vel p5.Vector

Stores the velocity vector for a firework or particle, determining how much its position changes each frame

this.vel = p5.Vector.sub(this.target, this.pos);
acc p5.Vector

Stores the acceleration vector for a firework or particle. For particles, gravity is added to this each frame

this.acc = createVector(0, 0);
exploded boolean

Flag indicating whether a firework has reached its target and exploded into particles

this.exploded = false;
particles array

Array of Particle objects created when a firework explodes. Particles are removed as they fade out

this.particles = [];
hu number

Stores the hue value (0-255 in HSB mode) for a firework and all its particles, ensuring consistent coloring

this.hu = random(255);
lifespan number

Tracks a particle's remaining opacity (0-255). Decreases each frame to create the fade effect

this.lifespan = 255;

๐Ÿงช Try This!

Experiment with the code by making these changes:

  1. Change the gravity value from 0.2 to 0.5 in setup() to make particles fall much faster. Try 0.05 to make them fall slowly and float more
  2. Modify the firework speed range from random(8, 12) to random(4, 6) in the Firework constructor to make fireworks travel slower to their target
  3. Change the particle count from random(50, 100) in the explode() method to random(200, 300) to create massive explosions with many more particles
  4. Adjust the fade speed by changing this.lifespan -= 5 to this.lifespan -= 2 in the Particle update() method to make particles fade more slowly
  5. Modify the explosion threshold from if (d < 10) to if (d < 50) in the Firework update() method to make fireworks explode earlier, before reaching the exact click point
  6. Change the background alpha from background(0, 25) to background(0, 100) in draw() to make trails disappear faster, or try background(0, 5) for longer trails
  7. Experiment with particle spread speed by changing this.vel.mult(random(1, 8)) to this.vel.mult(random(2, 15)) in the Particle constructor for more explosive spread
Open in Editor & Experiment โ†’

๐Ÿ”ง Potential Improvements

Here are some ways this code could be enhanced:

PERFORMANCE draw() function

The sketch redraws the semi-transparent background every frame, which can be computationally expensive on large canvases or low-end devices. With many fireworks and particles, this could cause frame rate drops

๐Ÿ’ก Consider using a graphics buffer (createGraphics) for the background layer, or implement a frame rate check to ensure smooth performance. Alternatively, only redraw the background every other frame on slower devices

BUG Firework constructor

Fireworks always start from the bottom of the canvas (height), which means clicking near the bottom creates very short travel distances. This can cause visual inconsistency

๐Ÿ’ก Consider starting fireworks from a random height between 0 and height/2, or add a parameter to control the starting height for more variety

FEATURE Particle class

All particles fade at the same rate (lifespan -= 5), creating uniform fade timing. Real fireworks have particles that fade at different rates

๐Ÿ’ก Add a fade rate property to each particle initialized as random(2, 8) in the constructor, then use this.lifespan -= this.fadeRate in the update method for more natural fading

STYLE Firework and Particle classes

Magic numbers like 10 (explosion threshold), 50-100 (particle count), and 5 (fade rate) are hardcoded throughout the classes, making them difficult to adjust globally

๐Ÿ’ก Define these as class properties or constants at the top of the sketch (e.g., const EXPLOSION_THRESHOLD = 10, const PARTICLE_COUNT_MIN = 50) for easier tweaking

FEATURE Particle class show() method

All particles are drawn as simple points with the same stroke weight (2), which can make the display look flat and uniform

๐Ÿ’ก Vary particle size based on lifespan (larger when fresh, smaller when fading) or add a size property to particles for more visual interest

BUG mousePressed() function

The function doesn't prevent default behavior, so clicking on the canvas might trigger unwanted browser actions or text selection

๐Ÿ’ก Add return false; at the end of mousePressed() to prevent default mouse behavior, or use return false in the function body

Preview

Click to Launch Fireworks - xelsed.ai - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Click to Launch Fireworks - xelsed.ai - Code flow showing setup, draw, mousepressed, windowresized, firework, particle
Code Flow Diagram