Building a Fireworks Animation with HTML5 Canvas and JavaScript


Fireworks are a captivating visual spectacle, and you can bring this excitement to your website by creating a dynamic fireworks animation using HTML5 Canvas and JavaScript. In this tutorial, we’ll explore a step-by-step guide to understand and implement the provided code.

Before we get start you can download the project files below:

Demo

Take a look at what the code creates:

Requirements and prerequisites

The code is intended to be run in a web browser. Open the HTML file in a modern web browser such as Google Chrome, Mozilla Firefox, Safari, or Microsoft Edge.

Make sure that JavaScript is enabled in your web browser, as the entire animation is implemented in JavaScript.

The code relies on the HTML5 Canvas API. Ensure that the browser supports HTML5 and has Canvas API support.

The code can be hosted on a web server or executed locally. If you are running it locally, you may encounter security restrictions (CORS issues) when loading external scripts or assets. Hosting it on a server can help avoid such issues.

HTML Setup

Firstly, set up your HTML file by creating a canvas element where the fireworks will come to life.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
    canvas {
      display: block;
    }
  </style>
  <title>Fireworks Animation</title>
</head>
<body>
  <canvas id="fireworksCanvas"></canvas>
  <script src="library.js"></script>
  <script src="fireworks.js"></script>
</body>
</html>

JavaScript Setup

The code below creates objects the different parts of the fireworks such as the particle and fireworks objects. In the example files, this code is within the "library.js" file.

//fireworks object to create a fireworks
const fireworksObject = function(x,y,target){
this.x =x;
this.y=y;
this.direction=null;
this.target={x:target.x,y:target.y}
//number of particles to release on explosion;
this.count=20;
this.speed=10;
this.particle_life=[10,30];
this.particle_speed=[1,5];
this.segments=[];
this.segment_limit=5;
this.init()
}
fireworksObject.prototype={
init:function(){
    
    this.segments.push({x:this.x,y:this.y});
    var dx=this.target.x-this.x;
    var dy=this.target.y-this.y;
    var magnitude=Math.sqrt(dx*dx+dy*dy);
    //normalized direction
    var nx=dx/magnitude;
    var ny=dy/magnitude;

    this.direction={x:nx,y:ny};
},
explode:function(fireworks,particles,canvas){
    var index=fireworks.findIndex(item =>item ==this);
    fireworks.splice(index,1);
    var i=0;
    while(i<this.count){
        var x0=Math.random(0,1)*canvas.width;
        var y0=Math.random(0,1)*canvas.height;
        var life=Math.random(0,1)*this.particle_life[1]+this.particle_life[0];
        var speed=Math.random(0,1)*this.particle_speed[1]+this.particle_speed[0];
        particles.push(create_particle(this.x,this.y,life,speed,{x:x0,y:y0}));
        i++;
    }
    delete(this);
},
move:function(){

    this.x+=this.speed*this.direction.x;
    this.y+=this.speed*this.direction.y;
}
,
update:function(gl,fireworks,particles,canvas){
    //move the fireworks
    this.move();
    //drawing the fire works;
    gl.beginPath();
    gl.strokeStyle=`hsl(${Math.random() * 5000}, 100%, 50%)`;
    gl.lineWidth=Math.random(0,1)*2+1;
    var last_pos=this.segments[this.segments.length-1];

    gl.moveTo(last_pos.x, last_pos.y);
    gl.lineTo(this.x,this.y);
 

    gl.stroke();
    gl.closePath();
    this.segments.push({x:this.x,y:this.y})
    if(this.segments.length>=this.segment_limit){
       this.segments.splice(0,1);
    }
    var dx=this.target.x-this.x;
    var dy=this.target.y-this.y;
    var distance=Math.sqrt(dx*dx+dy*dy)
    if(distance<=10){
        this.explode(fireworks,particles,canvas);
    }

}
}
function create_fireworks(x,y,target){
    return new fireworksObject(x,y,target);
}
//particle object to create particles from the fireworks
const particle = function(x,y,life,speed,target){
    this.x=x;
    this.y=y;
    this.life=life;
    this.decay=1;
    this.direction=null;
   
    this.target={x:target.x,y:target.y};
    this.speed=speed;
    this.gravity=2;
    this.segments=[];
    this.segment_limit=5;
    this.init();
    }
particle.prototype={
    init:function(){
    
        this.segments.push({x:this.x,y:this.y});
        var dx=this.target.x-this.x;
        var dy=this.target.y-this.y;
        var magnitude=Math.sqrt(dx*dx+dy*dy);
        //normalized direction
        var nx=dx/magnitude;
        var ny=dy/magnitude;
    
        this.direction={x:nx,y:ny};
    },
    end:function(particles){
        
        var index=particles.findIndex(item =>item ==this);
       
        particles.splice(index,1);
        delete(this);
    },
    move:function(){
    
        this.x+=this.speed*this.direction.x;
        this.y+=this.speed*this.direction.y+this.gravity;
    }
    ,
    update:function(gl,particles){
        //move the fireworks
        this.move();
        //drawing the fire works;
        gl.beginPath();
        gl.strokeStyle=`hsl(${Math.random() * 360}, 100%, 50%)`;
        gl.lineWidth=Math.random(0,1)*2+1;
        var last_pos=this.segments[this.segments.length-1];
    
        gl.moveTo(last_pos.x, last_pos.y);
        gl.lineTo(this.x,this.y);
     
    
        gl.stroke();
        gl.closePath();
        this.segments.push({x:this.x,y:this.y})
        if(this.segments.length>=this.segment_limit){
           this.segments.splice(0,1);
        }
        this.life-=this.decay;
        if(this.life<=0){
            this.end(particles);
        }
       
    
    }
}

function create_particle(x,y,life,speed,target){
    return new particle(x,y,life,speed,target);
}

Fireworks Object (fireworksObject):

  • The fireworksObject constructor initializes properties such as the initial position (x, y), target position, number of particles to release on explosion (count), speed, particle life range, particle speed range, segments, and segment limit.
  • The init method calculates the normalized direction vector towards the target.
  • The explode method removes the current fireworks from the array and creates a specified number of particles with random positions, life, and speed.
  • The move method updates the position of the fireworks based on its speed and direction.
  • The update method is responsible for updating the state of the fireworks, moving it, drawing its trajectory, and triggering an explosion when it reaches the target.

Particle Object (particle):

  • The particle constructor initializes properties similar to the fireworks object, including position, life, decay, direction, target, speed, gravity, segments, and segment limit.
  • The init method calculates the normalized direction vector towards the target.
  • The end method removes the particle from the array when its life is exhausted.
  • The move method updates the position of the particle based on its speed, direction, and gravity.
  • The update method updates the state of the particle, moving it, drawing its trajectory, and triggering its removal when its life is exhausted.

Additional Functions:

  • create_fireworks: A helper function to create a new instance of the fireworksObject.
  • create_particle: A helper function to create a new instance of the particle.

Animating the fireworks

The code below renders the fireworks on the screen by firing it from a random position the below the screen to a random point on the screen. It also creates an explosion once the fireworks reaches the a target position. The result is a set of particles render on screen moving and falling under gravity. This code is within the "fireworks.js" file.

document.addEventListener("DOMContentLoaded", function () {
    const canvas = document.getElementById("fireworksCanvas");
    const gl = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const refresh_rate=50;
    const particles=[];
    const fireworks=[];
    //delay to add fire works
    const delay=500;

    function clear(){
        gl.beginPath();
        gl.fillStyle="rgba(0,0,0,0.5)";
        gl.fillRect(0,0,canvas.width,canvas.height);
        gl.closePath()
    }
    function random_fireworks(){
         console.log("fired")
        //start position
        var x0=Math.random(0.3,0.8)*canvas.width;
        var y0=canvas.height+500;
        //target 
        var x1=Math.random(0.5,0.6)*canvas.width;
        var y1=Math.random(0.5,0.6)*canvas.height;
       
        fireworks.push(create_fireworks(x0,y0,{x:x1,y:y1}));
    }

    function update(){

      clear();
      for(i in particles){
         particles[i].update(gl,particles);
      }
      for(i in fireworks){
        fireworks[i].update(gl,fireworks,particles,canvas);
     }

     
      
      setTimeout(update,refresh_rate);
    }

    update();
    setInterval(random_fireworks,delay);
    
  });
  

Here is breakdown of the JavaScript code that animates the fireworks:

  1. Initialization:
    • The code waits for the DOM content to be fully loaded (DOMContentLoaded event).
    • It retrieves the canvas element with the id "fireworksCanvas" and sets its dimensions to match the window size.
    • A refresh rate (refresh_rate) is defined for the animation loop.
    • Arrays particles and fireworks are created to store instances of particle and fireworks objects.
  2. Canvas Clearing Function (clear):
    • The clear function is defined to clear the canvas on each animation frame. It fills the canvas with a semi-transparent black color, creating a fading effect to simulate trails.
  3. Random Fireworks Function (random_fireworks):
    • This function is responsible for creating random fireworks at a certain interval (delay).
    • It generates random start and target positions for the fireworks and adds a new fireworks object to the fireworks array using the create_fireworks helper function.
  4. Update Function (update):
    • The update function is the main animation loop.
    • It clears the canvas, updates and draws each particle in the particles array, and updates and draws each firework in the fireworks array.
    • The setTimeout function is used to call the update function at regular intervals (refresh_rate), creating a continuous animation loop.
  5. Initial Animation Start:
    • The update function is initially called to start the animation loop.
    • The random_fireworks function is set to be called at regular intervals to create new fireworks and keep the display dynamic.

Leave a Reply

Your email address will not be published. Required fields are marked *