Blog / JavaScript

Confetti, Fireworks and Particles

Create festive and memorable special effects for your interfaces: confetti, fireworks, snow, floating particles and sparkles in pure JavaScript.

Introduction

Festive special effects like confetti, fireworks and particles add a touch of magic to your interfaces. Whether celebrating a purchase, congratulating a user or simply beautifying a landing page, these visual animations create memorable moments.

In this tutorial, we will explore 5 different special effects in pure JavaScript: from explosive confetti to snowflakes, including Canvas fireworks. Each example is accompanied by the complete code, ready to copy into your project.

💡
Good to know

All the effects in this article use pure JavaScript, without any external library. They combine DOM approaches (animated HTML elements) and Canvas (pixel-by-pixel drawing) as needed.

1. Confetti Explosion

The confetti effect is perfect for celebrating successful actions: form validation, completed purchase, goal reached. We create colored DOM elements with random CSS animations.

The principle

We dynamically generate dozens of absolutely positioned <div> elements, each with a random color, direction and unique fall speed. The @keyframes CSS animation handles gravity and rotation.

confetti.js
function createConfetti() {
  const container = document.getElementById('confettiBox');
  const colors = [
    '#6366f1', '#8b5cf6', '#d946ef',
    '#f59e0b', '#10b981', '#ef4444'
  ];

  for (let i = 0; i < 50; i++) {
    const confetti = document.createElement('div');
    const color = colors[Math.floor(Math.random() * colors.length)];
    const size = Math.random() * 8 + 6;
    const left = Math.random() * 100;
    const delay = Math.random() * 0.5;
    const duration = 2 + Math.random() * 2;

    confetti.style.cssText = `
      position: absolute;
      width: ${size}px;
      height: ${size}px;
      background: ${color};
      left: ${left}%;
      top: 0;
      border-radius: ${Math.random() > 0.5 ? '50%' : '2px'};
      animation: confettiFall ${duration}s ease-out
                 ${delay}s forwards;
    `;

    container.appendChild(confetti);
    setTimeout(() => confetti.remove(), 4500);
  }
}

The associated CSS animation

confetti-keyframes.css
@keyframes confettiFall {
  0% {
    transform: translateY(0) rotate(0);
    opacity: 1;
  }
  100% {
    transform: translateY(300px) rotate(720deg);
    opacity: 0;
  }
}
💡
Tip

Use border-radius: 50% for round confetti and border-radius: 2px for rectangles. Alternating shapes makes the effect more natural.

2. Floating particles

Floating particles create an immersive atmosphere for your backgrounds. This effect uses the Canvas API to draw and animate dozens of particles with connections between them.

The principle

We initialize an array of particles with random positions and speeds. At each frame, we move the particles, draw them and trace lines between those that are close to each other.

particles.js
function initParticles(canvasId) {
  const canvas = document.getElementById(canvasId);
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;

  const particles = [];
  const count = 40;
  const maxDist = 120;

  for (let i = 0; i < count; i++) {
    particles.push({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      vx: (Math.random() - 0.5) * 0.8,
      vy: (Math.random() - 0.5) * 0.8,
      r: Math.random() * 2 + 1
    });
  }

  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    particles.forEach(p => {
      p.x += p.vx;
      p.y += p.vy;
      if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
      if (p.y < 0 || p.y > canvas.height) p.vy *= -1;

      ctx.beginPath();
      ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
      ctx.fillStyle = 'rgba(99, 102, 241, 0.6)';
      ctx.fill();
    });

    // Lines between nearby particles
    for (let i = 0; i < count; i++) {
      for (let j = i + 1; j < count; j++) {
        const dx = particles[i].x - particles[j].x;
        const dy = particles[i].y - particles[j].y;
        const dist = Math.sqrt(dx * dx + dy * dy);
        if (dist < maxDist) {
          ctx.strokeStyle =
            `rgba(99,102,241,${1 - dist/maxDist})`;
          ctx.lineWidth = 0.5;
          ctx.beginPath();
          ctx.moveTo(particles[i].x, particles[i].y);
          ctx.lineTo(particles[j].x, particles[j].y);
          ctx.stroke();
        }
      }
    }

    requestAnimationFrame(animate);
  }
  animate();
}
🔗
Connection lines

Lines between nearby particles create a mesh effect. Opacity decreases with distance for a natural result. Adjust maxDist to control the mesh density.

3. Sparkles / Sparkles

Sparkles are small luminous particles that follow the mouse cursor or appear around an element on hover. It is an elegant effect for drawing attention to a CTA or premium element.

Cursor trail

We listen to mouse movements and create mini luminous elements at each movement. Each sparkle grows then fades out.

sparkles.js
function initSparkles() {
  document.addEventListener('mousemove', (e) => {
    const sparkle = document.createElement('div');
    const size = Math.random() * 6 + 4;
    const colors = ['#fbbf24', '#f59e0b', '#fff'];
    const color = colors[
      Math.floor(Math.random() * colors.length)
    ];

    sparkle.style.cssText = `
      position: fixed;
      pointer-events: none;
      width: ${size}px;
      height: ${size}px;
      background: ${color};
      border-radius: 50%;
      left: ${e.clientX}px;
      top: ${e.clientY}px;
      box-shadow: 0 0 6px ${color};
      animation: sparkleFade 0.6s ease-out forwards;
      z-index: 9999;
    `;

    document.body.appendChild(sparkle);
    setTimeout(() => sparkle.remove(), 600);
  });
}

// Keyframes to add in CSS:
// @keyframes sparkleFade {
//   0%   { transform: scale(0); opacity: 1; }
//   50%  { transform: scale(1); opacity: 0.8; }
//   100% { transform: scale(0); opacity: 0; }
// }
⚠️
Performance warning

Creating a DOM element at each mousemove event can quickly become expensive. Use a throttle (for example a call every 50ms) to limit the number of sparkles created simultaneously.

4. Falling snow

The snow effect is ideal for winter themes, Christmas pages or simply for a soft and soothing atmosphere. We use Canvas to draw falling snowflakes with a slight horizontal movement.

Canvas implementation

Each snowflake has a random size, speed and horizontal offset. When a snowflake reaches the bottom of the canvas, it is replaced at the top for a continuous effect.

snow.js
function initSnow(canvasId) {
  const canvas = document.getElementById(canvasId);
  const ctx = canvas.getContext('2d');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  const flakes = [];
  const count = 100;

  for (let i = 0; i < count; i++) {
    flakes.push({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      r: Math.random() * 3 + 1,
      speed: Math.random() * 1 + 0.5,
      wind: Math.random() * 0.5 - 0.25,
      opacity: Math.random() * 0.5 + 0.3
    });
  }

  function animate() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    flakes.forEach(f => {
      f.y += f.speed;
      f.x += f.wind;

      // Reset to top if out of bounds
      if (f.y > canvas.height) {
        f.y = -5;
        f.x = Math.random() * canvas.width;
      }

      ctx.beginPath();
      ctx.arc(f.x, f.y, f.r, 0, Math.PI * 2);
      ctx.fillStyle =
        `rgba(255, 255, 255, ${f.opacity})`;
      ctx.fill();
    });

    requestAnimationFrame(animate);
  }
  animate();
}

Customization

  • Number of snowflakes: adjust count (50 for subtle, 200 for blizzard)
  • Fall speed: modify the multiplier in speed
  • Wind: change the wind range for more or less pronounced lateral movement
  • Opacity: vary to create a depth effect (nearby vs distant snowflakes)

5. Fireworks

The most spectacular effect: a firework that explodes from a central point with colored particles that fall back in an arc under the effect of gravity. We use Canvas with a simplified physics simulation.

Simplified physics

Each particle has an initial velocity in a random direction. At each frame, we apply gravity that pulls the particles downward, creating the natural arc of a firework.

fireworks.js
function initFireworks(canvasId) {
  const canvas = document.getElementById(canvasId);
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;

  let particles = [];
  const gravity = 0.03;

  function explode(x, y) {
    const count = 60;
    const hue = Math.random() * 360;

    for (let i = 0; i < count; i++) {
      const angle = (Math.PI * 2 / count) * i;
      const speed = Math.random() * 3 + 1;

      particles.push({
        x, y,
        vx: Math.cos(angle) * speed,
        vy: Math.sin(angle) * speed,
        life: 1,
        decay: Math.random() * 0.01 + 0.005,
        color: `hsl(${hue + Math.random()*30}, 100%, 60%)`
      });
    }
  }

  function animate() {
    // Semi-transparent background for trail effect
    ctx.fillStyle = 'rgba(10, 10, 15, 0.15)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    particles.forEach(p => {
      p.x += p.vx;
      p.y += p.vy;
      p.vy += gravity;  // Gravite
      p.life -= p.decay;

      ctx.beginPath();
      ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
      ctx.globalAlpha = p.life;
      ctx.fillStyle = p.color;
      ctx.fill();
    });

    ctx.globalAlpha = 1;
    // Clean up dead particles
    particles = particles.filter(p => p.life > 0);

    requestAnimationFrame(animate);
  }

  // Click to launch firework
  canvas.addEventListener('click', (e) => {
    const rect = canvas.getBoundingClientRect();
    explode(e.clientX - rect.left, e.clientY - rect.top);
  });

  animate();
}
💡
Trail effect

Instead of completely clearing the canvas with clearRect, we draw a semi-transparent rectangle (rgba(10, 10, 15, 0.15)) at each frame. This leaves a luminous trail behind each particle, a very realistic effect for fireworks.

Best practices

Before deploying these effects in production, here are some essential recommendations to ensure a smooth experience:

Performance

  • Always use requestAnimationFrame instead of setInterval for continuous animations. The browser optimizes rendering and pauses the animation when the tab is inactive.
  • Limit the number of particles: 50 to 100 are sufficient for a nice effect. Beyond 200, performance drops on mobile.
  • Prefer Canvas over DOM for large quantities of particles. Manipulating hundreds of DOM elements is much more expensive than drawing on a canvas.

Cleanup and memory

  • Remove dead particles with filter() or manage a pool of reusable particles to avoid memory leaks.
  • Clean up listeners when the component is unmounted (important in SPAs with React, Vue, etc.).
  • Use cancelAnimationFrame to stop animations when they are no longer visible.

Mobile and accessibility

responsive-check.js
// Reduce particles on mobile
const isMobile = window.innerWidth < 768;
const particleCount = isMobile ? 25 : 60;

// Respect prefers-reduced-motion
const prefersReduced = window
  .matchMedia('(prefers-reduced-motion: reduce)')
  .matches;

if (!prefersReduced) {
  initParticles('myCanvas', particleCount);
}
⚠️
Accessibility

Always check prefers-reduced-motion before launching automatic animations. Some users suffer from vestibular disorders and animations can cause nausea or dizziness.

Conclusion

Festive special effects are an excellent way to make your interfaces more lively and memorable. Whether celebrating a user action with confetti, creating a winter atmosphere with snow or impressing with fireworks, JavaScript provides all the power needed.

The DOM approach (confetti, sparkles) is ideal for punctual effects with few elements. The Canvas approach (particles, snow, fireworks) is preferable when the number of elements increases. In all cases, always think about performance, memory cleanup and accessibility.

🎨
Go further

Find special effects ready to use in our effects library, with one-click copyable code and real-time customization.