Générateur de Feux d'Artifice au Clic
Create click fireworks, confetti and particle bursts for your site, then copy the JavaScript
100 % gratuit. Fonctionne entièrement dans votre navigateur : vos fichiers et données ne quittent jamais votre appareil et rien n'est envoyé à un serveur.
local_fire_department Click canvas
Click anywhere to spawn the effect.
code Export
/* Click fireworks - generated by 500 Toolbox
* Attach to the whole page (window):
* document.addEventListener('click', handler)
* Attach to one element:
* document.querySelector('.ugfw-btn').addEventListener('click', handler)
*
* Rendering notes: glow uses cached radial-gradient sprites and additive
* blending instead of ctx.shadowBlur, which is what keeps a 900-particle
* sky at 60fps.
*/
(function () {
var CFG = {"kind":"fireworks","count":90,"size":2.6,"speed":6.2,"gravity":0.11,"spread":360,"colors":["#fbbf24","#ef4444","#ffffff"],"glow":true,"duration":1500,"direction":"out","randomness":0.7,"emoji":"✨","attach":"window","launch":true,"trails":true,"autoShow":true,"max":900};
var canvas = document.getElementById('ugfw-canvas');
if (!canvas) {
canvas = document.createElement('canvas');
canvas.id = 'ugfw-canvas';
document.body.appendChild(canvas);
}
var ctx = canvas.getContext('2d');
if (!ctx) return;
var ps = [];
var last = 0;
var W = window.innerWidth, H = window.innerHeight;
var sprites = {};
function fit() {
var dpr = Math.min(2, window.devicePixelRatio || 1);
W = window.innerWidth;
H = window.innerHeight;
canvas.width = W * dpr;
canvas.height = H * dpr;
canvas.style.width = '100%';
canvas.style.height = '100%';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
fit();
window.addEventListener('resize', fit);
function pick() { return CFG.colors[Math.floor(Math.random() * CFG.colors.length)]; }
function sprite(color) {
if (sprites[color]) return sprites[color];
var c = document.createElement('canvas');
c.width = 64; c.height = 64;
var g = c.getContext('2d');
var grd = g.createRadialGradient(32, 32, 0, 32, 32, 32);
grd.addColorStop(0, '#ffffff');
grd.addColorStop(0.22, color);
grd.addColorStop(0.55, color);
grd.addColorStop(1, 'rgba(0,0,0,0)');
g.fillStyle = grd;
g.beginPath(); g.arc(32, 32, 32, 0, Math.PI * 2); g.fill();
sprites[color] = c;
return c;
}
function star(r) {
ctx.beginPath();
for (var i = 0; i < 10; i++) {
var a = (i * Math.PI) / 5 - Math.PI / 2;
var rad = i % 2 ? r * 0.38 : r;
var x = Math.cos(a) * rad, y = Math.sin(a) * rad;
if (i) ctx.lineTo(x, y); else ctx.moveTo(x, y);
}
ctx.closePath();
}
function heart(sz) {
ctx.beginPath();
ctx.moveTo(0, sz * 0.35);
ctx.bezierCurveTo(0, -sz * 0.3, -sz, -sz * 0.3, -sz, sz * 0.12);
ctx.bezierCurveTo(-sz, sz * 0.62, 0, sz, 0, sz);
ctx.bezierCurveTo(0, sz, sz, sz * 0.62, sz, sz * 0.12);
ctx.bezierCurveTo(sz, -sz * 0.3, 0, -sz * 0.3, 0, sz * 0.35);
}
function trailKind(k) {
return k === 'fireworks' || k === 'spark' || k === 'neon' || k === 'glow' || k === 'magic' || k === 'stars';
}
function lightning(x, y) {
var dir = (Math.random() - 0.5) * (CFG.spread * Math.PI / 180);
var len = 80 + Math.random() * 140;
var pts = [{ x: x, y: y }];
var cx = x, cy = y, segs = 8 + Math.floor(Math.random() * 6);
for (var i = 0; i < segs; i++) {
cx += Math.sin(dir) * (len / segs) + (Math.random() - 0.5) * 28;
cy += Math.cos(dir) * (len / segs) + Math.random() * 10;
pts.push({ x: cx, y: cy });
}
return { role: 'spark', kind: 'lightning', x: x, y: y, vx: 0, vy: 0, life: CFG.duration * 0.5, maxLife: CFG.duration * 0.6, size: CFG.size, color: pick(), rot: 0, vr: 0, points: pts, emoji: '' };
}
function shell(x, targetY) {
var startY = H + 8;
var rise = Math.max(60, startY - targetY);
var frames = Math.max(22, Math.min(90, rise / 7));
var g = Math.max(0.02, CFG.gravity) * 0.55;
var v0 = ((targetY - startY) - 0.5 * g * frames * frames) / frames;
var lifeMs = frames * 16 + 400;
return { role: 'shell', kind: CFG.kind, x: x, y: startY, vx: (Math.random() - 0.5) * 1.1, vy: v0,
life: lifeMs, maxLife: lifeMs, size: Math.max(1.6, CFG.size * 0.8), color: pick(),
rot: 0, vr: 0, emoji: CFG.emoji, targetY: targetY, trail: [x, startY], seed: Math.random() * 6.28 };
}
function one(x, y, i, n, shellColor) {
var spreadRad = CFG.spread * Math.PI / 180;
var round = CFG.direction === 'out' && CFG.spread >= 340;
var angle;
if (round) angle = Math.random() * Math.PI * 2;
else if (CFG.direction === 'out') angle = (i / n) * Math.PI * 2 + (Math.random() - 0.5) * spreadRad * CFG.randomness;
else if (CFG.direction === 'up') angle = -Math.PI / 2 + (Math.random() - 0.5) * spreadRad;
else if (CFG.direction === 'down') angle = Math.PI / 2 + (Math.random() - 0.5) * spreadRad;
else angle = Math.random() * Math.PI * 2;
var bias = round ? 0.45 + 0.55 * Math.sqrt(Math.random()) : 0.55 + Math.random() * 0.7;
var sp = CFG.speed * bias * (1 - 0.35 * CFG.randomness + 0.35 * CFG.randomness * (0.6 + Math.random() * 0.8));
var vx = Math.cos(angle) * sp, vy = Math.sin(angle) * sp;
if (CFG.kind === 'bubbles' || CFG.kind === 'smoke') { vy = -Math.abs(vy) * (CFG.kind === 'smoke' ? 0.45 : 0.7); vx *= 0.45; }
if (CFG.kind === 'snow' || CFG.kind === 'leaves') { vy = Math.abs(vy) * 0.35 + 0.4; vx *= 0.5; }
var life = CFG.duration * (0.65 + Math.random() * 0.5);
return { role: 'spark', kind: CFG.kind, x: x, y: y, vx: vx, vy: vy, life: life, maxLife: life,
size: CFG.size * (0.6 + Math.random() * 0.9),
color: Math.random() < 0.16 ? '#fff6de' : shellColor,
rot: Math.random() * Math.PI * 2, vr: (Math.random() - 0.5) * 0.28, emoji: CFG.emoji,
seed: Math.random() * 6.28,
trail: CFG.trails && trailKind(CFG.kind) ? [x, y] : null };
}
function explode(x, y) {
var room = Math.max(0, CFG.max - ps.length);
if (room < 12) { ps.splice(0, Math.min(ps.length, CFG.count)); room = Math.max(0, CFG.max - ps.length); }
var n = Math.min(room, CFG.count);
var shellColor = pick();
if (CFG.glow) ps.push({ role: 'flash', kind: CFG.kind, x: x, y: y, vx: 0, vy: 0, life: 190, maxLife: 190, size: CFG.size * 9, color: '#ffffff', rot: 0, vr: 0, emoji: '' });
for (var i = 0; i < n; i++) ps.push(one(x, y, i, n, shellColor));
}
function burst(x, y) {
if (CFG.kind === 'lightning') {
for (var b = 0; b < 4; b++) ps.push(lightning(x, y));
return;
}
if (CFG.launch) { ps.push(shell(x, y)); return; }
explode(x, y);
}
function pushTrail(p) {
if (!p.trail) return;
p.trail.push(p.x, p.y);
if (p.trail.length > 12) p.trail.splice(0, p.trail.length - 12);
}
function tick(now) {
var dt = last ? Math.min(48, now - last) : 16;
last = now;
var f = dt / 16;
ctx.clearRect(0, 0, W, H);
var next = [];
for (var i = 0; i < ps.length; i++) {
var p = ps[i];
p.life -= dt;
if (p.role === 'shell') {
p.vy += CFG.gravity * 0.55 * f;
p.x += p.vx * f; p.y += p.vy * f;
pushTrail(p);
if (p.vy >= 0 || p.y <= p.targetY || p.life <= 0) { explode(p.x, p.y); continue; }
next.push(p);
continue;
}
if (p.life <= 0) continue;
if (p.kind === 'lightning' || p.role === 'flash') { next.push(p); continue; }
p.vy += CFG.gravity * f;
if (p.kind === 'snow' || p.kind === 'leaves') p.x += Math.sin(p.life / 90 + p.rot) * 0.6;
if (p.kind === 'bubbles') p.x += Math.sin(p.life / 70) * 0.35;
p.x += p.vx * f; p.y += p.vy * f; p.rot += p.vr * f;
var drag = p.trail ? 0.976 : 0.992;
p.vx *= drag;
if (p.trail) p.vy *= drag;
pushTrail(p);
next.push(p);
}
ps = next;
ctx.globalCompositeOperation = CFG.glow ? 'lighter' : 'source-over';
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
for (var j = 0; j < ps.length; j++) {
var q = ps[j];
var t = Math.max(0, Math.min(1, q.life / q.maxLife));
if (q.role === 'flash') {
var fr = q.size * (1 + (1 - t) * 1.6);
ctx.globalAlpha = t * t * 0.75;
ctx.drawImage(sprite(q.color), q.x - fr, q.y - fr, fr * 2, fr * 2);
continue;
}
if (q.kind === 'lightning') {
ctx.globalAlpha = t;
ctx.strokeStyle = q.color;
ctx.lineWidth = Math.max(1.2, q.size * 0.8);
ctx.beginPath();
ctx.moveTo(q.points[0].x, q.points[0].y);
for (var k = 1; k < q.points.length; k++) ctx.lineTo(q.points[k].x, q.points[k].y);
ctx.stroke();
continue;
}
var flicker = q.seed === undefined ? 1 : 0.72 + 0.28 * Math.sin(q.life * 0.045 + q.seed);
var alpha = Math.min(1, t * 1.25) * (t < 0.55 ? flicker : 1);
if (q.trail && q.trail.length >= 4) {
ctx.globalAlpha = alpha * 0.42;
ctx.strokeStyle = q.color;
ctx.lineWidth = Math.max(0.8, q.size * 0.85);
ctx.beginPath();
ctx.moveTo(q.trail[0], q.trail[1]);
for (var m = 2; m < q.trail.length; m += 2) ctx.lineTo(q.trail[m], q.trail[m + 1]);
ctx.stroke();
}
if (q.role === 'shell') {
var sr = q.size * 2.2;
ctx.globalAlpha = 0.9;
ctx.drawImage(sprite(q.color), q.x - sr, q.y - sr, sr * 2, sr * 2);
continue;
}
if (q.kind === 'confetti' || q.kind === 'pixel' || q.kind === 'stars' || q.kind === 'magic'
|| q.kind === 'hearts' || q.kind === 'emoji' || q.kind === 'leaves') {
ctx.save();
ctx.globalAlpha = alpha;
ctx.fillStyle = q.color;
ctx.translate(q.x, q.y);
ctx.rotate(q.rot);
if (q.kind === 'confetti') ctx.fillRect(-q.size, -q.size * 0.4, q.size * 2, q.size * 1.2);
else if (q.kind === 'pixel') ctx.fillRect(-q.size, -q.size, q.size * 2, q.size * 2);
else if (q.kind === 'stars' || q.kind === 'magic') { star(q.size * 1.6); ctx.fill(); }
else if (q.kind === 'hearts') { heart(q.size * 1.3); ctx.fill(); }
else if (q.kind === 'emoji') { ctx.font = (q.size * 3.2) + 'px serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(q.emoji, 0, 0); }
else { ctx.beginPath(); ctx.ellipse(0, 0, q.size * 1.6, q.size * 0.7, 0.4, 0, Math.PI * 2); ctx.fill(); }
ctx.restore();
} else if (q.kind === 'smoke') {
ctx.globalAlpha = t * 0.26; ctx.fillStyle = q.color;
ctx.beginPath(); ctx.arc(q.x, q.y, q.size * (2.2 - t), 0, Math.PI * 2); ctx.fill();
} else if (q.kind === 'bubbles') {
ctx.globalAlpha = alpha; ctx.strokeStyle = q.color; ctx.lineWidth = 1.2;
ctx.beginPath(); ctx.arc(q.x, q.y, q.size * 1.4, 0, Math.PI * 2); ctx.stroke();
} else if (q.kind === 'snow') {
ctx.globalAlpha = alpha; ctx.fillStyle = q.color;
ctx.beginPath(); ctx.arc(q.x, q.y, q.size, 0, Math.PI * 2); ctx.fill();
} else {
var rr = q.size * (q.kind === 'glow' ? 2.6 : q.kind === 'neon' ? 2.2 : 1.9);
ctx.globalAlpha = alpha;
if (CFG.glow) ctx.drawImage(sprite(q.color), q.x - rr, q.y - rr, rr * 2, rr * 2);
else { ctx.fillStyle = q.color; ctx.beginPath(); ctx.arc(q.x, q.y, q.size * 1.1, 0, Math.PI * 2); ctx.fill(); }
}
}
ctx.globalCompositeOperation = 'source-over';
ctx.globalAlpha = 1;
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
if (CFG.autoShow) {
setInterval(function () {
if (document.hidden) return;
burst(W * (0.15 + Math.random() * 0.7), H * (0.18 + Math.random() * 0.3));
}, 1100);
}
function handler(e) { burst(e.clientX, e.clientY); }
if (CFG.attach === 'button') {
var btn = document.querySelector('.ugfw-btn');
if (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var r = e.currentTarget.getBoundingClientRect();
burst(r.left + r.width / 2, r.top + r.height / 2);
});
}
} else {
document.addEventListener('click', handler);
}
})();À propos de cet outil
Click fireworks turn a tap into a burst of particles: sparks, confetti, hearts, emoji, snow, neon. This generator lets you click the preview, tune count, gravity, spread and colour, then copy JavaScript that attaches to the whole page or to one button. A particle cap and a performance mode keep low-end phones from melting. Effects never autoplay audio and nothing is uploaded.
Comment utiliser cet outil
- Choose an effectFireworks, confetti, stars, hearts, emoji, smoke, bubbles, snow and more.
- Click the previewEach click bursts particles. Tune count, size, speed, gravity and colours.
- Pick where it attachesEvery click on the page, or only a specific button. Then copy the script.
Pourquoi l’utiliser
- Fifteen particle styles with live click testing.
- Presets for celebration, gaming, magic, love, neon, minimal and party.
- Particle cap plus a performance mode for slower devices.
- Export attaches to the window or to one element.
Usages courants
- A celebrate burst when someone finishes a form.
- Confetti on a pricing-page upgrade click.
- A quiet spark on a game UI.
Erreurs à éviter
- Using ctx.shadowBlur for the glow. It re-renders a blur per particle per frame and is the usual reason a burst drops frames; cached gradient sprites cost a fraction of it.
- Attaching the effect to every click on the page, including clicks on links and form fields, where it fights the actual interaction.
- Setting particle count high and duration long at the same time. Live particles are count multiplied by lifetime, not count alone.
- Firing celebration effects without checking prefers-reduced-motion, which can genuinely affect people with vestibular disorders.
- Leaving the canvas without pointer-events: none, so it silently swallows clicks meant for the page underneath.
Questions fréquentes
The engine caps live particles (about 400) and performance mode halves the burst. Old particles are removed when they fade.
Yes. For emoji and hearts you can type a custom symbol that the burst will use.
Switch to the element option, copy the script, and keep the sample selector or point it at your own button id.
No. The export is a small vanilla script. No extra package to install.
Les gens recherchent aussi
- click fireworks javascript
- confetti effect on click
- canvas particle effect generator
- celebration animation for website
- js confetti library alternative
- mouse click effect javascript
- firework animation code