Skip to main content

Command Palette

Search for a command to run...

Designing a CSS-Animated Fireworks Display with JavaScript Timing

Published
11 min readView as Markdown
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:

  1. HTML - To create the structure and containers for our fireworks

  2. CSS - To handle the animations and visual styling

  3. 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:

  1. A dark night sky background using a gradient

  2. Controls styling at the bottom of the screen

  3. The firework elements (rockets and explosion particles)

  4. Key animations:

    • launch - For the initial rocket trajectory

    • explosion - For the burst effect

    • particle-explosion - For individual particles of the firework

    • twinkle - 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:

  1. Initialization - Setting up event listeners and creating background stars

  2. Show Control - Functions to start and stop the fireworks show

  3. Firework Creation - Dynamically creating firework elements and animations

  4. Explosion Effects - Different types of explosions with particle systems

  5. Timing Control - Managing the sequence and duration of events

Understanding the Firework Life Cycle

Each firework goes through a carefully timed sequence:

  1. Creation: A firework element is created at a random position along the bottom of the screen

  2. Launch: The rocket animates upward using CSS animation

  3. Explosion: Just before the rocket animation completes, it's removed and replaced with an explosion

  4. Particles: Multiple particles animate outward from the explosion point

  5. 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:

  1. Limit Active Elements: Too many simultaneous fireworks can slow down the browser

  2. Use CSS Transforms: They're more performant than changing position properties

  3. Cleanup Old Elements: Always remove firework elements after they complete

  4. Use RequestAnimationFrame: For more complex timing needs instead of setTimeout

  5. 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!

More from this blog

L

Learn Computer Academy

50 posts

Website Design and Development Training Center in Habra. We offer top-notch practical training in Graphics Design, Website Design, and Development.