<div class="click-area" id="click-area">
<canvas id="confetti-canvas"></canvas>
<div class="pointer-text">
<h2>Celebrate Success</h2>
<p>Click anywhere on the screen to trigger an explosion.</p>
</div>
</div>
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
margin: 0;
background: #ffffff;
overflow: hidden;
}
.click-area {
position: relative;
width: 100vw;
height: 100vh;
cursor: pointer;
background: radial-gradient(circle at center, #f8fafc 0%, #e2e8f0 100%);
}
#confetti-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.pointer-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
z-index: 1;
pointer-events: none;
}
.pointer-text h2 {
font-size: 3rem;
color: #0f172a;
margin: 0 0 10px 0;
letter-spacing: -1px;
}
.pointer-text p {
font-size: 1.25rem;
color: #64748b;
margin: 0;
}
const canvas = document.getElementById("confetti-canvas");
const ctx = canvas.getContext("2d");
const clickArea = document.getElementById("click-area");
let width = window.innerWidth;
let height = window.innerHeight;
canvas.width = width;
canvas.height = height;
window.addEventListener("resize", function() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
});
const particles = new Array();
function getConfettiColor() {
const rand = Math.random();
if (rand < 0.2) return "#3b82f6";
if (rand < 0.4) return "#ec4899";
if (rand < 0.6) return "#10b981";
if (rand < 0.8) return "#f59e0b";
return "#8b5cf6";
}
function spawnExplosion(x, y) {
const count = 40;
for (let i = 0; i < count; i++) {
const p = new Object();
p.x = x;
p.y = y;
p.color = getConfettiColor();
p.size = Math.random() * 8 + 4;
/* Random circular velocity */
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 10 + 5;
p.vx = Math.cos(angle) * speed;
p.vy = Math.sin(angle) * speed;
p.rotation = Math.random() * 360;
p.rotSpeed = (Math.random() - 0.5) * 10;
p.gravity = 0.3;
p.friction = 0.96;
p.opacity = 1;
particles.push(p);
}
}
clickArea.addEventListener("click", function(e) {
spawnExplosion(e.clientX, e.clientY);
});
function animate() {
ctx.clearRect(0, 0, width, height);
for (let i = 0; i < particles.length; i++) {
const p = particles.at(i);
/* Apply physics */
p.vx *= p.friction;
p.vy *= p.friction;
p.vy += p.gravity;
p.x += p.vx;
p.y += p.vy;
p.rotation += p.rotSpeed;
/* Fade out over time */
p.opacity -= 0.01;
if (p.opacity > 0) {
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate((p.rotation * Math.PI) / 180);
ctx.fillStyle = p.color;
ctx.globalAlpha = p.opacity;
ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);
ctx.restore();
}
}
/* Filter out dead particles natively without array bracket syntax */
while (particles.length > 0 && particles.at(0).opacity <= 0) {
particles.shift();
}
window.requestAnimationFrame(animate);
}
animate();