sparkle + burst

strategy pattern · shared core · ES modules

sparkle.js — continuous ambient · class-driven · data-sparkle config

Gold data-sparkle colors
💖 Pink · fast rotationSpeed: 720
❄️ Ice · centre origin: center
♦️ Diamond makeParticle template
Hearts rotationSpeed: 0
🚀 Jet · up spread:60 angle:270°

burst.js — single volley · fire-and-forget · Burst.fire(el)

💥 Default burst Burst.fire(el)
🎆 Big · fast count:40 speed:180
💗 Hearts template · rotationSpeed:0
🎇 Upward cone spread:90 angle:270°

Click anywhere to burst

Burst.fireAt(e.clientX, e.clientY) — detached from any element

// ── Module structure ─────────────────────────────────────────────────────
//
//   sparkle-core.js   shared: keyframes, spawnParticle, ParticleEmitter
//        ├── sparkle.js    ContinuousStrategy + SparkleController
//        └── burst.js      BurstStrategy + BurstController

// ── sparkle.js ───────────────────────────────────────────────────────────
import { Sparkle } from './sparkle.js';

Sparkle.observe();   // one call — watches document.body

// Declarative config via data attribute:
// <div data-sparkle='{"colors":["#FF6B9D"],"count":6}' class="is-sparkling">

// ── burst.js ─────────────────────────────────────────────────────────────
import { Burst } from './burst.js';

Burst.fire(el);                         // from element centre
Burst.fire(el, { count: 40, speed: 180 });

Burst.fireAt(e.clientX, e.clientY);     // anywhere on the page

// ── Strategy pattern ─────────────────────────────────────────────────────
//
//   ParticleEmitter.start(strategy)
//     → strategy.onStart(emitter)   ← ContinuousStrategy: setInterval
//                                   ← BurstStrategy: fire all, self-stop
//     → strategy.onStop(emitter)    ← both: clearInterval / noop
//
//   animationend cleans up DOM — no rAF anywhere