library / celebration / falling-confetti
live - 60fps 1920 x 1080
falling-confetti.html - self-contained
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Falling Confetti</title>
    <style>
      /*
        Falling confetti animation
        --------------------------
        This page draws a shower of confetti using the HTML5 canvas.  Each
        piece of confetti is represented by a small circle whose colour and
        drift are chosen at random.  As the pieces fall they oscillate left
        and right to give the impression of air resistance.  When they
        leave the bottom of the screen they are recycled at the top.
      */
      html, body {
        margin: 0;
        padding: 0;
        height: 100%;
        overflow: hidden;
        background: #0c0c0c;
        font-family: sans-serif;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <canvas id="confetti"></canvas>
    <script>
      (function() {
        const canvas = document.getElementById('confetti');
        const ctx    = canvas.getContext('2d');

        // Resize the canvas to fill the window.
        function resize() {
          canvas.width  = window.innerWidth;
          canvas.height = window.innerHeight;
        }
        window.addEventListener('resize', resize);
        resize();

        // List of pastel colours.  These hues are similar to those used in
        // the original CodePen; they provide a cheerful, celebratory look.
        const colours = [
          '#ff5978', '#ff9b44', '#f6d743', '#54c571', '#57c7ff', '#b18fff'
        ];

        // Number of confetti pieces.  Increase for denser showers at the
        // expense of frame rate.
        const COUNT = 150;

        // Confetti particle definition.
        class Confetto {
          constructor() {
            this.reset();
          }
          reset() {
            this.x     = Math.random() * canvas.width;
            this.y     = -10 - Math.random() * canvas.height;
            this.size  = 6 + Math.random() * 6;
            this.speed = 1 + Math.random() * 2;
            this.wave  = 50 + Math.random() * 50;
            this.phase = Math.random() * Math.PI * 2;
            this.col   = colours[Math.floor(Math.random() * colours.length)];
          }
          update(dt) {
            // Vertical motion
            this.y += this.speed * dt;
            // Horizontal oscillation
            this.x += Math.sin(this.phase + this.y / this.wave) * 0.5;
            // When the piece falls past the bottom, recycle it at the top.
            if (this.y > canvas.height + this.size) {
              this.reset();
              this.y = -this.size;
            }
          }
          draw(ctx) {
            ctx.fillStyle = this.col;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
            ctx.fill();
          }
        }

        // Create the confetti.
        const confetti = [];
        for (let i = 0; i < COUNT; i++) {
          confetti.push(new Confetto());
        }

        // Track time between frames for consistent motion across
        // different refresh rates.
        let lastTime = null;
        function tick(timestamp) {
          if (!lastTime) lastTime = timestamp;
          const dt = (timestamp - lastTime) / 16.666; // relative to ~60fps
          lastTime = timestamp;

          ctx.clearRect(0, 0, canvas.width, canvas.height);
          // Draw a subtle gradient background for depth.
          const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
          grad.addColorStop(0, '#151515');
          grad.addColorStop(1, '#0a0a0a');
          ctx.fillStyle = grad;
          ctx.fillRect(0, 0, canvas.width, canvas.height);

          // Update and render each piece of confetti.
          for (const piece of confetti) {
            piece.update(dt);
            piece.draw(ctx);
          }
          requestAnimationFrame(tick);
        }
        requestAnimationFrame(tick);
      })();
    </script>
  </body>
</html>

About this animation

Celebratory falling confetti pieces.

Under the hood

DOM-Based Falling Physics

Unlike canvas confetti, this utilizes floating DOM
nodes paired with native CSS @keyframes that push elements down the Y-axis while simultaneously spinning them on the Z-axis, ensuring high performance across devices without needing a canvas context.

Source Code
Source copied
Partner hosting options for the copied effect:
Vercel Netlify Hostinger