Created
March 4, 2026 18:30
-
-
Save derak-kilgo/ee6d6d9675fe2c41ed199365c29123ed to your computer and use it in GitHub Desktop.
xss confetti
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script> | |
| (function () { | |
| // Avoid double-injection | |
| if (window.__confettiRunning) return; | |
| window.__confettiRunning = true; | |
| // Create canvas | |
| var cv = document.createElement('canvas'); | |
| var ctx = cv.getContext('2d'); | |
| cv.style.position = 'fixed'; | |
| cv.style.top = 0; | |
| cv.style.left = 0; | |
| cv.style.width = '100%'; | |
| cv.style.height = '100%'; | |
| cv.style.pointerEvents = 'none'; | |
| cv.style.zIndex = 2147483647; // on top | |
| document.documentElement.appendChild(cv); | |
| // Resize handling | |
| function resize() { | |
| cv.width = window.innerWidth; | |
| cv.height = window.innerHeight; | |
| } | |
| window.addEventListener('resize', resize); | |
| resize(); | |
| // Particle factory | |
| var TAU = Math.PI * 2; | |
| var colors = ['#E91E63','#9C27B0','#3F51B5','#2196F3','#009688','#4CAF50','#FFC107','#FF5722','#795548','#00BCD4']; | |
| function rand(min, max){ return Math.random()*(max-min)+min; } | |
| function pick(arr){ return arr[(Math.random()*arr.length)|0]; } | |
| function makeParticle() { | |
| return { | |
| x: rand(0, cv.width), | |
| y: rand(-cv.height*0.2, -20), | |
| vx: rand(-1.2, 1.2), | |
| vy: rand(2.0, 5.0), | |
| size: rand(6, 12), | |
| color: pick(colors), | |
| angle: rand(0, TAU), | |
| spin: rand(-0.25, 0.25), | |
| drag: rand(0.985, 0.995), | |
| tilt: rand(-0.7, 0.7), | |
| life: 0, | |
| ttl: rand(180, 360) // frames | |
| }; | |
| } | |
| // Burst configuration | |
| var particles = []; | |
| var maxParticles = 220; // cap | |
| var emission = 60; // per burst | |
| var bursts = 6; // how many bursts | |
| var burstGap = 180; // frames between bursts | |
| var frame = 0; | |
| var gravity = 0.06; | |
| var windBase = rand(-0.02, 0.02); | |
| function emit(count) { | |
| for (var i = 0; i < count && particles.length < maxParticles; i++) { | |
| var p = makeParticle(); | |
| // Launch near top-center | |
| p.x = cv.width/2 + rand(-cv.width*0.15, cv.width*0.15); | |
| p.vx += rand(-1.6, 1.6); | |
| p.vy = rand(0.5, 3.5); | |
| particles.push(p); | |
| } | |
| } | |
| // Kick off first burst immediately | |
| emit(emission); | |
| var rafId; | |
| function tick() { | |
| frame++; | |
| // Periodic bursts | |
| if (bursts > 0 && frame % burstGap === 0) { | |
| emit(emission); | |
| bursts--; | |
| } | |
| // Wind oscillation | |
| var wind = windBase + Math.sin(frame/90) * 0.06; | |
| // Clear | |
| ctx.clearRect(0, 0, cv.width, cv.height); | |
| // Update & draw | |
| for (var i = particles.length - 1; i >= 0; i--) { | |
| var p = particles[i]; | |
| p.vx += wind * 0.1; | |
| p.vy += gravity; | |
| p.vx *= p.drag; | |
| p.vy *= p.drag; | |
| p.x += p.vx; | |
| p.y += p.vy; | |
| p.angle += p.spin; | |
| p.life++; | |
| // Wrap horizontally | |
| if (p.x < -20) p.x = cv.width + 20; | |
| if (p.x > cv.width + 20) p.x = -20; | |
| // Remove if out of view or expired | |
| if (p.y > cv.height + 40 || p.life > p.ttl) { | |
| particles.splice(i, 1); | |
| continue; | |
| } | |
| // Draw as tilted rectangle (simulating flutter) | |
| var w = p.size; | |
| var h = p.size * (0.6 + 0.4 * Math.sin(p.angle * 1.7)); | |
| ctx.save(); | |
| ctx.translate(p.x, p.y); | |
| ctx.rotate(p.angle + p.tilt); | |
| ctx.fillStyle = p.color; | |
| ctx.fillRect(-w/2, -h/2, w, h); | |
| ctx.restore(); | |
| } | |
| // Stop when animation is done (no particles, no bursts pending) | |
| if (particles.length === 0 && bursts <= 0) { | |
| cleanup(); | |
| return; | |
| } | |
| rafId = requestAnimationFrame(tick); | |
| } | |
| function cleanup() { | |
| cancelAnimationFrame(rafId); | |
| window.removeEventListener('resize', resize); | |
| if (cv && cv.parentNode) cv.parentNode.removeChild(cv); | |
| window.__confettiRunning = false; | |
| } | |
| // Auto-start after load (or immediately if already loaded) | |
| if (document.readyState === 'complete' || document.readyState === 'interactive') { | |
| requestAnimationFrame(tick); | |
| } else { | |
| window.addEventListener('DOMContentLoaded', function(){ requestAnimationFrame(tick); }, { once: true }); | |
| } | |
| })(); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment