
Create a confetti animation with HTML canvas and JavaScript
Have you ever clicked a button and seen a confetti animation explode onto the screen? Or visited a website where the whole screen is covered in that same confetti animation? Let’s make our own. In this tutorial, we’re going to make some dynamic confetti effects with JavaScript and the HTML Canvas API.
What we’re working towards
Let’s have a look at the final confetti demo we’re going to build.
Introduction to HTML canvas and canvas API
In web development, the <canvas>
element allows us to draw shapes directly onto the web page. We can also animate them to create dynamic effects. Take a look at this example:
Here we’ve set up a simple animation where the blue circle moves around the screen, detecting collisions and bouncing off the edges.
To create a confetti animation, we’ll use exactly the same concept used to move the ball. We’ll draw rectangular shapes with random colors and then apply some slight movement, shifting each rectangle’s position with every frame.
Setting up the HTML
First, we’ll need a basic structure containing the <canvas/>
element and some text at the center of the canvas. This is all the HTML code we need.
1 | <canvas id="canvas"></canvas> |
2 | <div id="cheers">Cheers!</div> |
Apply styles with CSS
Now add these styles:
1 | @import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); |
2 | |
3 | body { |
4 | margin: 0; |
5 | overflow: hidden; |
6 | background: #1a1a1a; |
7 | display: flex; |
8 | justify-content: center; |
9 | align-items: center; |
10 | min-height: 100vh; |
11 | font-family: "DM Mono", monospace; |
12 | } |
13 | |
14 | #cheers { |
15 | position: absolute; |
16 | top: 50%; |
17 | left: 50%; |
18 | transform: translate(-50%, -50%); |
19 | font-size: 3rem; |
20 | color: #ff5733; |
21 | font-weight: bold; |
22 | opacity: 0; |
23 | animation: animateText 4s ease-in-out infinite; |
24 | z-index: 10; |
25 | } |
26 | |
27 | @keyframes animateText { |
28 | 0% { |
29 | opacity: 0; |
30 | transform: translate(-50%, 50vh); |
31 | } |
32 | 50% { |
33 | opacity: 1; |
34 | transform: translate(-50%, -50%); |
35 | } |
36 | 100% { |
37 | opacity: 0; |
38 | transform: translate(-50%, -150%); |
39 | } |
40 | } |
Here we’ve animated the cheers text by making it move from the bottom of the screen to the center, before fading out, creating a visually appealing effect.
Creating the confetti animation with JavaScript
Now, let’s bring the confetti to life with JavaScript. We have seen how to simulate the effect of movement with the blue circle. The idea of the confetti is to generate random colorful rectangles that move and rotate in random directions downwards.
Let’s start by getting the canvas element.
1 | const canvas = document.getElementById("canvas"); |
Next, we’ll create the rendering context.Â
1 | const ctx = canvas.getContext("2d"); |
The context is what allows us to draw on the canvas area, and it also provides methods for drawing on the canvas. Next, create an array called particles
. This array will store the properties of the rectangles.
1 | const particles = []; |
Create a function called resizeCanvas()
which will ensure the canvas resizes to match the height and width of the browser window. This ensures that whenever the window is resized, the canvas dimensions will sync with the viewport.
1 | function resizeCanvas() { |
2 | canvas.width = window.innerWidth; |
3 | canvas.height = window.innerHeight; |
4 | } |
5 | |
6 | resizeCanvas(); |
7 | window.addEventListener("resize", resizeCanvas); |
This function will then be called whenever the resize event occurs ensuring the canvas always fills the available space.
Create confetti particles
Each particle is represented by a rectangleand will have the following properties.
-
x – The current horizontal position of the particle.
- y – The current vertical position of the particle. It starts off-screen above the canvas.
- size – The size of the particle in pixels, randomly set between 3 and 11 pixels.
- speedX – The horizontal speed of the particle
- speedY – The vertical speed of the particle.
- rotation – The rotation angle in radians
- color – The color of the particle
The next thing we want to do is define the value of each property and store it in our particle array.
1 | function createParticle() { |
2 | particles.push({ |
3 | x: Math.random() * canvas.width, |
4 | y: Math.random() * canvas.height - canvas.height, |
5 | size: Math.random() * 6 + 3, |
6 | speedX: (Math.random() - 0.5) * 4, |
7 | speedY: Math.random() * 4 + 2, |
8 | rotation: (Math.random() - 0.5) * 5, |
9 | color: `hsl(${Math.random() * 360}, 80%, 60%)` |
10 | }); |
11 | } |
The createParticle()
function will generate key-value pairs for each particle and add them to the particles array. To ensure no two particles are the same, we are using Math.random()
to assign each particle a unique position, size, speed, rotation, and color. This will ensure a natural effect when the particles are animated.
Draw the particles
Once all the properties are defined, it’s time to draw the particles on the canvas and apply translation and rotation methods. Create a function called drawParticle()
which takes a particle object as an argument. This function will be responsible for drawing each particle on the canvas.Â
1 | function drawParticle(particle) { |
2 | |
3 | } |
Save the current state of the canvas to ensure translation or rotation applied to one particle doesn’t affect other particles.
1 | function drawParticle(particle) { |
2 | ctx.save(); |
3 | } |
When drawing a particle, we’ll follow these steps:
- Start drawing at its x position(current position).Â
- Add a slight rotation at a random angle, to ensure all particles don’t look the same
- Give each particle a unique color
- Draw a rectangle to represent each confetti piece.
1 | function drawParticle(particle) { |
2 | ctx.save(); |
3 | ctx.translate(particle.x, particle.y); |
4 | ctx.rotate(particle.rotation); |
5 | ctx.fillStyle = particle.color; |
6 | ctx.fillRect(-particle.size, -particle.size / 4, particle.size * 2, particle.size / 2); |
7 | |
8 | } |
Finally, reset the transformation to ensure the next piece is not affected.
1 | function drawParticle(particle) { |
2 | ctx.save(); |
3 | ctx.translate(particle.x, particle.y); |
4 | ctx.rotate(particle.rotation); |
5 | ctx.fillStyle = particle.color; |
6 | |
7 | ctx.fillRect(-particle.size, -particle.size / 4, particle.size * 2, particle.size / 2); |
8 | |
9 | ctx.restore(); |
10 | } |
Generate particles
The confetti animation is achieved by creating multiple particles. To do this, let’s create a loop that calls the createParticle()
function 200 times.Â
Every time the loop runs, a new unique particle is added to the particle array. This ensures that each particle has its own random position, size, color, and movement direction creating a more natural effect.
1 | for(let i = 0; i < 200; i++) { |
2 | createParticle(); |
3 | } |
To create the confetti effect and see it on the canvas, we need to continuously draw and redraw the parts on the screen creating an illusion of movement. To achieve this effect, we need to animate on every frame.Â
To create animations in the canvas, the canvas API provides the requestAnimationFrame()
method which works by scheduling a function to run before the next repaint of the screen. Since it syncs with the browser’s refresh rate, it’s perfect for creating smooth animations
1 | function animate() { |
2 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
3 | particles.forEach((particle) => { |
4 | drawParticle(particle); |
5 | particle.x += particle.speedX; |
6 | particle.y += particle.speedY; |
7 | }); |
8 | |
9 | requestAnimationFrame(animate); |
10 | } |
11 | |
12 | animate(); |
On each frame, we draw the particle and update its position with particle.x+=particle.speedX;
 and particle.x+=particle.speedX;
.
The requestAnimationFrame()
method can be considered as an optimized looping mechanism that ensures that particles move smoothly. Due to the random properties applied to each particle, the movement will appear smooth creating a leaf-falling effect. Â
To achieve this continuous movement, it’s important to call the animate function() recursively. Â
So far, the confetti particles fall downward in one direction—we need to reset each particle’s position to the top once it reaches the bottom. This will create a continuous loop effect.
Update the animation function as follows:
1 | function animate() { |
2 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
3 | particles.forEach((particle) => { |
4 | drawParticle(particle); |
5 | particle.x += particle.speedX; |
6 | particle.y += particle.speedY; |
7 | particle.x += Math.sin(particle.y * 0.1) * Math.random() * 0.5; |
8 | |
9 | if(particle.y > canvas.height + 20) { |
10 | particle.y = Math.random() * canvas.height - canvas.height; |
11 | particle.x = Math.random() * canvas.width; |
12 | } |
13 | }); |
14 | |
15 | requestAnimationFrame(animate); |
16 | } |
For a more natural and dynamic falling effect, lets’s add a sinusoidal wave.
1 | function animate() { |
2 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
3 | particles.forEach((particle) => { |
4 | drawParticle(particle); |
5 | particle.x += particle.speedX; |
6 | particle.y += particle.speedY; |
7 | particle.x += Math.sin(particle.y * 0.1) * Math.random() * 0.5; |
8 | |
9 | if(particle.y > canvas.height + 20) { |
10 | particle.y = Math.random() * canvas.height - canvas.height; |
11 | particle.x = Math.random() * canvas.width; |
12 | } |
13 | }); |
14 | |
15 | requestAnimationFrame(animate); |
16 | } |
And we’re done!
Here is the final demo of our confetti animation!
We have created a vibrant, dynamic confetti effect with HTML Canvas and Vanilla JavaScript. Other variations you can build include exploding confetti for a sudden burst effect, bouncing effect and so on. Have fun with it!