>_htmlhub
library/celebration/fizzy-sparks

Fizzy Sparks

Live previewPerformance varies by device
fizzy-sparks.html

No account needed to copy or download. Check dependencies and thelicense status before publishing.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Fizzy Sparks</title>
    <style>
      /*
        Fizzy Sparks
        ------------
        This interactive canvas spawns small, glowing sparks wherever the
        user moves or clicks the mouse.  Each spark travels in a random
        direction and fades out over time.  The effect approximates the
        original CodePen's fizzy sparks animation using only plain
        JavaScript and the Canvas API.
      */
      html, body {
        margin: 0;
        padding: 0;
        height: 100%;
        overflow: hidden;
        background: #000;
        cursor: crosshair;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <canvas id="sparks"></canvas>
    <script>
      (function() {
        const canvas = document.getElementById('sparks');
        const ctx    = canvas.getContext('2d');
        function resize() {
          canvas.width  = window.innerWidth;
          canvas.height = window.innerHeight;
        }
        window.addEventListener('resize', resize);
        resize();
        // Particle class representing a single spark.
        class Spark {
          constructor(x, y) {
            this.x     = x;
            this.y     = y;
            const angle= Math.random() * Math.PI * 2;
            const speed= 2 + Math.random() * 3;
            this.vx    = Math.cos(angle) * speed;
            this.vy    = Math.sin(angle) * speed;
            this.life  = 40 + Math.floor(Math.random() * 20);
            this.age   = 0;
            this.radius= 3 + Math.random() * 4;
            // Choose a bright, random colour.
            const hue = Math.floor(Math.random() * 360);
            this.colour = `hsl(${hue}, 90%, 70%)`;
          }
          update() {
            this.x += this.vx;
            this.y += this.vy;
            // Apply a small amount of friction.
            this.vx *= 0.98;
            this.vy *= 0.98;
            // Gravity to pull sparks downwards.
            this.vy += 0.05;
            this.age++;
          }
          draw(ctx) {
            const alpha = 1 - this.age / this.life;
            ctx.fillStyle = this.colour;
            ctx.globalAlpha = alpha;
            ctx.shadowBlur = 12;
            ctx.shadowColor = this.colour;
            ctx.beginPath();
            ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
            ctx.fill();
            ctx.shadowBlur = 0;
            ctx.globalAlpha = 1;
          }
          isDead() {
            return this.age >= this.life;
          }
        }
        const sparks = [];
        function spawn(x, y, count) {
          for (let i = 0; i < count; i++) {
            sparks.push(new Spark(x, y));
          }
        }
        // Spawn sparks on mouse movement and clicks.
        let lastSpawn = 0;
        document.addEventListener('mousemove', (e) => {
          const now = Date.now();
          if (now - lastSpawn > 50) {
            spawn(e.clientX, e.clientY, 5);
            lastSpawn = now;
          }
        });
        document.addEventListener('click', (e) => {
          spawn(e.clientX, e.clientY, 20);
        });
        const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
        let frameCount = 0;
        function animate() {
          frameCount++;
          // Gentle idle emission: sparse ambient sparks so the resting frame isn't black.
          if (!reducedMotion && frameCount % 8 === 0) {
            spawn(Math.random() * canvas.width, Math.random() * canvas.height, 2);
          }
          // Draw a translucent black rectangle to create trails.
          ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          // Update and draw each spark.
          for (let i = sparks.length - 1; i >= 0; i--) {
            const s = sparks[i];
            s.update();
            s.draw(ctx);
            if (s.isDead()) {
              sparks.splice(i, 1);
            }
          }
          if (!reducedMotion) requestAnimationFrame(animate);
        }
        if (reducedMotion) {
          // Static frame: one representative burst at the centre, no loop.
          spawn(canvas.width / 2, canvas.height / 2, 40);
          animate();
        } else {
          animate();
        }
      })();
    </script>
</body>
</html>

About this animation

Rising bubbles and sparks, great for success states.

Under the hood

Interactive Canvas Sparks

Sparks spawn at the cursor on Canvas 2D, 5 per throttled mousemove event and 20 per click, each launched in a random direction at a random speed and hue. Friction scales velocity by 0.98, gravity adds 0.05 to vertical velocity so they fall, and they fade via globalAlpha over a 40 to 60 frame life, with no bottom spawning, buoyancy, or sine-wave jitter. Even with no mouse input, two sparks spawn at a random position every 8 frames as gentle ambient emission, and each spark is drawn with a shadowBlur glow in its own colour.

Source Code
Copied to clipboard