Generador de Fuegos Artificiales al Clic
Create click fireworks, confetti and particle bursts for your site, then copy the JavaScript
100% gratis. Funciona completamente en tu navegador: tus archivos y datos nunca salen de tu dispositivo y no se sube nada a ningún servidor.
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);
}
})();Acerca de esta herramienta
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.
Cómo usar esta herramienta
- 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.
Por qué usarla
- 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.
Usos comunes
- A celebrate burst when someone finishes a form.
- Confetti on a pricing-page upgrade click.
- A quiet spark on a game UI.
Errores a evitar
- 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.
Preguntas frecuentes
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.
La gente también busca
- 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