Designing a CSS-Animated Fireworks Display with JavaScript Timing

Creating a visually stunning fireworks display on your website can be a fun way to celebrate special occasions or simply add some flair to your user interface. In this comprehensive guide, we'll walk through building a CSS-animated fireworks display with JavaScript timing controls, giving you complete control over the show.
The End Result
Before diving into the code, let's clarify what we're building:
Multiple colored fireworks that launch and explode
CSS animations for smooth, performant visual effects
JavaScript timing to control the firework sequences
Customizable colors, sizes, and explosion patterns
No external dependencies or libraries needed
Understanding the Core Concepts
Our fireworks display relies on three key technologies:
HTML - To create the structure and containers for our fireworks
CSS - To handle the animations and visual styling
JavaScript - To manage timing, create firework elements dynamically, and coordinate the display
The approach works by creating DOM elements for each firework, applying CSS animations to them, and using JavaScript to time and coordinate the fireworks' appearance and disappearance.
Step 1: Setting Up the HTML Structure
Let's start with a simple HTML structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS-Animated Fireworks Display</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="night-sky">
<div class="controls">
<button id="start-show">Launch Fireworks</button>
<div class="options">
<label>
<input type="checkbox" id="random-mode" checked>
Random Mode
</label>
<label>
<input type="range" id="frequency" min="1" max="10" value="5">
Frequency
</label>
<label>
<input type="range" id="duration" min="10" max="60" value="30">
Duration (seconds)
</label>
</div>
</div>
</div>
<script src="fireworks.js"></script>
</body>
</html>
This creates a container for our night sky where the fireworks will appear, along with some basic controls to start the show and adjust parameters.
Step 2: Creating the CSS Animations
Now, let's define our CSS styles and animations:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
overflow: hidden;
}
.night-sky {
position: relative;
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom, #000 0%, #000033 70%);
overflow: hidden;
}
.controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
color: white;
z-index: 100;
}
.options {
display: flex;
gap: 20px;
margin-top: 10px;
}
button {
padding: 8px 16px;
background: #ff4500;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #ff6a33;
}
label {
display: flex;
align-items: center;
gap: 5px;
}
input[type="range"] {
width: 100px;
}
/* Firework styles */
.firework {
position: absolute;
bottom: -10px;
width: 10px;
height: 10px;
border-radius: 50%;
transform-origin: center bottom;
}
.rocket {
position: absolute;
bottom: 0;
width: 3px;
height: 10px;
background: rgba(255, 255, 255, 0.5);
transform-origin: center bottom;
animation-timing-function: ease-out;
}
.particle {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
transform-origin: center center;
opacity: 1;
z-index: 1;
}
/* Keyframe Animations */
@keyframes launch {
0% {
transform: translateY(0) scale(1);
opacity: 1;
}
70% {
opacity: 1;
}
100% {
transform: translateY(-100vh) scale(0.8);
opacity: 0;
}
}
@keyframes explosion {
0% {
opacity: 1;
transform: scale(0.1);
}
50% {
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}
@keyframes particle-explosion {
0% {
transform: translate(0, 0) scale(1);
opacity: 1;
}
100% {
transform: translate(var(--tx), var(--ty)) scale(0);
opacity: 0;
}
}
/* Optional twinkle effect for stars */
.star {
position: absolute;
width: 2px;
height: 2px;
background: white;
border-radius: 50%;
animation: twinkle 4s infinite alternate;
opacity: 0.7;
}
@keyframes twinkle {
0%, 100% {
opacity: 0.7;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.3);
}
}
This CSS file defines:
A dark night sky background using a gradient
Controls styling at the bottom of the screen
The firework elements (rockets and explosion particles)
Key animations:
launch- For the initial rocket trajectoryexplosion- For the burst effectparticle-explosion- For individual particles of the fireworktwinkle- For background stars
Step 3: Creating the JavaScript Logic
Now for the core functionality with JavaScript:
document.addEventListener('DOMContentLoaded', () => {
const nightSky = document.querySelector('.night-sky');
const startBtn = document.getElementById('start-show');
const randomModeCheckbox = document.getElementById('random-mode');
const frequencySlider = document.getElementById('frequency');
const durationSlider = document.getElementById('duration');
let fireworksInterval;
let showDuration;
// Add some stars to the background
createStars(100);
// Event listener for starting the fireworks show
startBtn.addEventListener('click', () => {
if (startBtn.textContent === 'Launch Fireworks') {
startFireworksShow();
startBtn.textContent = 'Stop Show';
} else {
stopFireworksShow();
startBtn.textContent = 'Launch Fireworks';
}
});
function startFireworksShow() {
const frequency = 1000 / frequencySlider.value; // Convert to milliseconds
showDuration = durationSlider.value * 1000; // Convert to milliseconds
// Launch fireworks at the specified frequency
fireworksInterval = setInterval(() => {
launchFirework();
}, frequency);
// Set a timeout to stop the show after the specified duration
if (showDuration > 0) {
setTimeout(() => {
stopFireworksShow();
startBtn.textContent = 'Launch Fireworks';
}, showDuration);
}
}
function stopFireworksShow() {
clearInterval(fireworksInterval);
}
function launchFirework() {
// Random position along the bottom of the screen
const randomX = Math.random() * window.innerWidth;
// Create firework container
const firework = document.createElement('div');
firework.className = 'firework';
firework.style.left = `${randomX}px`;
// Random color for this firework
const hue = Math.floor(Math.random() * 360);
const color = `hsl(${hue}, 100%, 60%)`;
// Create rocket element
const rocket = document.createElement('div');
rocket.className = 'rocket';
rocket.style.backgroundColor = color;
// Add rocket to firework container
firework.appendChild(rocket);
// Add firework to the night sky
nightSky.appendChild(firework);
// Set rocket launch animation
const launchDuration = 1000 + Math.random() * 1000; // 1-2 second launch
const launchHeight = 30 + Math.random() * 60; // Random height (30-90% of screen)
rocket.style.animation = `launch ${launchDuration / 1000}s forwards`;
// Explosion timing
setTimeout(() => {
// Remove the rocket
rocket.remove();
// Create explosion element
createExplosion(firework, color, randomX, launchHeight);
// Remove the entire firework element after explosion completes
setTimeout(() => {
firework.remove();
}, 2000); // Explosion lasts 2 seconds
}, launchDuration - 100); // Explode just before rocket animation ends
}
function createExplosion(firework, color, x, launchHeight) {
// Create an explosion wrapper
const explosion = document.createElement('div');
explosion.className = 'explosion';
explosion.style.position = 'absolute';
// Position the explosion at the top of the rocket's path
explosion.style.bottom = `${launchHeight}vh`;
explosion.style.left = '0';
firework.appendChild(explosion);
// Determine explosion type
const explosionType = randomModeCheckbox.checked ?
Math.floor(Math.random() * 3) : 0;
// Create particles based on explosion type
let particleCount;
switch (explosionType) {
case 0: // Circle explosion
particleCount = 30;
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2;
createParticle(explosion, color, angle);
}
break;
case 1: // Double circle explosion
particleCount = 40;
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2;
createParticle(explosion, color, angle);
if (i % 2 === 0) {
createParticle(explosion, color, angle, 0.6);
}
}
break;
case 2: // Star explosion
particleCount = 5;
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2;
createParticle(explosion, color, angle, 1.0);
// Add points between arms for star shape
const midAngle = angle + (Math.PI / particleCount);
createParticle(explosion, color, midAngle, 0.5);
}
break;
}
}
function createParticle(explosion, color, angle, scale = 1.0) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.backgroundColor = color;
// Random distance for this particle
const distance = 50 + Math.random() * 50; // 50-100px
// Calculate the translation values for this particle
const tx = Math.cos(angle) * distance * scale;
const ty = Math.sin(angle) * distance * scale;
// Set CSS variables for the animation
particle.style.setProperty('--tx', `${tx}px`);
particle.style.setProperty('--ty', `${ty}px`);
// Set animation
const duration = 1 + Math.random() * 1; // 1-2 seconds
particle.style.animation = `particle-explosion ${duration}s forwards`;
explosion.appendChild(particle);
}
function createStars(count) {
for (let i = 0; i < count; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = `${Math.random() * 100}%`;
star.style.top = `${Math.random() * 100}%`;
// Randomize animation delay and duration for twinkling effect
star.style.animationDelay = `${Math.random() * 4}s`;
star.style.animationDuration = `${2 + Math.random() * 3}s`;
nightSky.appendChild(star);
}
}
});
This JavaScript includes:
Initialization - Setting up event listeners and creating background stars
Show Control - Functions to start and stop the fireworks show
Firework Creation - Dynamically creating firework elements and animations
Explosion Effects - Different types of explosions with particle systems
Timing Control - Managing the sequence and duration of events
Understanding the Firework Life Cycle
Each firework goes through a carefully timed sequence:
Creation: A firework element is created at a random position along the bottom of the screen
Launch: The rocket animates upward using CSS animation
Explosion: Just before the rocket animation completes, it's removed and replaced with an explosion
Particles: Multiple particles animate outward from the explosion point
Cleanup: After all animations complete, the elements are removed from the DOM
Customizing the Fireworks Display
Let's explore some ways to customize your fireworks:
1. Adding Different Colors
You can create multi-colored fireworks by modifying the createExplosion function:
function createExplosion(firework, baseColor, x, launchHeight) {
// ... existing code
// Determine if this should be a multi-colored explosion
const isMultiColored = Math.random() > 0.7; // 30% chance
// Create particles
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2;
// For multi-colored explosions, give each particle a different color
let particleColor = baseColor;
if (isMultiColored) {
const hue = (parseInt(baseColor.split('(')[1]) + (i * 20)) % 360;
particleColor = `hsl(${hue}, 100%, 60%)`;
}
createParticle(explosion, particleColor, angle);
}
}
2. Adding Sound Effects
For a more immersive experience, add sound effects:
// Add to the top of your JavaScript file
const launchSound = new Audio('launch.mp3');
const explosionSound = new Audio('explosion.mp3');
// Then in the launchFirework function
function launchFirework() {
// Play launch sound
launchSound.currentTime = 0;
launchSound.volume = 0.3;
launchSound.play();
// ... existing code
// In the explosion timeout
setTimeout(() => {
// Play explosion sound
explosionSound.currentTime = 0;
explosionSound.volume = 0.4;
explosionSound.play();
// ... existing explosion code
}, launchDuration - 100);
}
3. Creating Shaped Explosions
You can create more complex explosion patterns by modifying the createExplosion function:
// Add a new case to the switch statement in createExplosion
case 3: // Heart shape
particleCount = 40;
for (let i = 0; i < particleCount; i++) {
const t = (i / particleCount) * Math.PI * 2;
// Heart curve equation
const x = 16 * Math.pow(Math.sin(t), 3);
const y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);
// Scale and flip the shape
const angle = Math.atan2(-y, x);
const distance = Math.sqrt(x*x + y*y) * 3;
createCustomParticle(explosion, color, angle, distance);
}
break;
Performance Considerations
When creating fireworks animations, keep these performance tips in mind:
Limit Active Elements: Too many simultaneous fireworks can slow down the browser
Use CSS Transforms: They're more performant than changing position properties
Cleanup Old Elements: Always remove firework elements after they complete
Use RequestAnimationFrame: For more complex timing needs instead of setTimeout
Test on Mobile: Make sure your animations run smoothly on lower-powered devices
Advanced Enhancements
Here are some ideas to take your fireworks display to the next level:
1. Sequential Patterns
Create fireworks that launch in specific patterns:
function launchSequence(pattern) {
switch (pattern) {
case 'wave':
for (let i = 0; i < 10; i++) {
setTimeout(() => {
launchFirework(i * (window.innerWidth / 10));
}, i * 200);
}
break;
case 'center':
// Launch multiple fireworks from the center
const centerX = window.innerWidth / 2;
for (let i = 0; i < 5; i++) {
setTimeout(() => {
launchFirework(centerX);
}, i * 300);
}
break;
}
}
2. Special Occasion Messages
Create text messages with firework particles:
function createTextFirework(text) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas size and text properties
canvas.width = 400;
canvas.height = 200;
ctx.font = '60px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
// Get the pixel data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
// Create particles for bright pixels
const particles = [];
const step = 4; // Only sample some pixels for performance
for (let y = 0; y < canvas.height; y += step) {
for (let x = 0; x < canvas.width; x += step) {
const index = (y * canvas.width + x) * 4;
const alpha = pixels[index + 3];
// If this is part of the text (non-transparent)
if (alpha > 128) {
particles.push({
x: x - canvas.width / 2,
y: y - canvas.height / 2
});
}
}
}
// Now use the particle positions to create your firework
const explosion = document.createElement('div');
explosion.className = 'text-explosion';
explosion.style.position = 'absolute';
explosion.style.left = '50%';
explosion.style.top = '50%';
explosion.style.transform = 'translate(-50%, -50%)';
// Random color for this text explosion
const hue = Math.floor(Math.random() * 360);
const color = `hsl(${hue}, 100%, 60%)`;
// Create particles
particles.forEach(pos => {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.backgroundColor = color;
// Set CSS variables for the animation
particle.style.setProperty('--tx', `${pos.x}px`);
particle.style.setProperty('--ty', `${pos.y}px`);
// Set animation
particle.style.animation = `text-particle 2s forwards`;
explosion.appendChild(particle);
});
nightSky.appendChild(explosion);
// Remove the explosion after animation completes
setTimeout(() => {
explosion.remove();
}, 2000);
}
Optimizing for Different Screen Sizes
To make your fireworks display responsive, add these enhancements:
// Add to the top of your JavaScript file
let screenWidth, screenHeight;
function updateScreenDimensions() {
screenWidth = window.innerWidth;
screenHeight = window.innerHeight;
}
// Call this on load and resize
window.addEventListener('resize', updateScreenDimensions);
updateScreenDimensions();
// Then modify your launchFirework function
function launchFirework(x = null) {
// If no x position provided, generate a random one
const randomX = x !== null ? x : Math.random() * screenWidth;
// Scale firework size based on screen dimensions
const scale = Math.min(screenWidth, screenHeight) / 1000;
// ... rest of function
// Apply scale to elements
firework.style.transform = `scale(${scale})`;
// Adjust animation duration based on screen height
const launchDuration = (screenHeight / 1000) * (1000 + Math.random() * 1000);
}
Conclusion
Creating a CSS-animated fireworks display with JavaScript timing gives you a performant, customizable way to add visual flair to your website. The approach we've taken combines the power of CSS animations with the control of JavaScript timing to create a stunning effect.
You can extend this system in countless ways:
Create different particle shapes and behaviors
Add physics simulations for more realistic motion
Synchronize the fireworks with music
Create interactive elements that users can trigger
The key advantage of using CSS for the animations is performance - the browser can optimize these animations to run smoothly even on less powerful devices.
I hope you've enjoyed this tutorial and are inspired to create your own dazzling displays!






