Skip to content

Instantly share code, notes, and snippets.

@leetrout
Created February 7, 2026 16:43
Show Gist options
  • Select an option

  • Save leetrout/306ac336de1fc41c0d40dee1c3adb279 to your computer and use it in GitHub Desktop.

Select an option

Save leetrout/306ac336de1fc41c0d40dee1c3adb279 to your computer and use it in GitHub Desktop.
An HTML file to use with OBS to shower the screen with emoji / pics
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image Confetti Shower</title>
<style>
* { margin: 0; padding: 0; }
body {
overflow: hidden;
background: transparent;
width: 100vw;
height: 100vh;
}
.confetti {
position: fixed;
top: -120px;
will-change: transform;
pointer-events: none;
}
</style>
</head>
<body>
<script>
// ============ CONFIGURATION ============
const CONFIG = {
// Add your image URLs or local paths here
images: [
// 'https://example.com/image.png',
],
// If no images above, generate colorful emoji confetti as placeholders
placeholderEmojis: ['πŸŽ‰', '⭐', 'πŸ”₯', 'πŸ’œ', '🎊', 'πŸ’Ž', '🌟', 'πŸ•', '🎈', 'πŸ†'],
spawnRate: 150, // ms between spawns (lower = more dense)
minSize: 30, // px
maxSize: 80, // px
minDuration: 3000, // ms to cross screen (lower = faster)
maxDuration: 7000,
minRotation: -720, // degrees total spin
maxRotation: 720,
swayAmount: 120, // px horizontal sway
maxOnScreen: 80, // limit for performance
};
// =======================================
let activeCount = 0;
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function createPlaceholder(emoji, size) {
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.font = `${size * 0.8}px serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(emoji, size / 2, size / 2);
return canvas.toDataURL();
}
function spawnConfetti() {
if (activeCount >= CONFIG.maxOnScreen) return;
const size = rand(CONFIG.minSize, CONFIG.maxSize);
const el = document.createElement('img');
el.className = 'confetti';
if (CONFIG.images.length > 0) {
el.src = CONFIG.images[Math.floor(Math.random() * CONFIG.images.length)];
} else {
const emoji = CONFIG.placeholderEmojis[Math.floor(Math.random() * CONFIG.placeholderEmojis.length)];
el.src = createPlaceholder(emoji, Math.round(size));
}
el.style.width = size + 'px';
el.style.height = size + 'px';
const startX = rand(-size, window.innerWidth);
const sway = rand(-CONFIG.swayAmount, CONFIG.swayAmount);
const rotation = rand(CONFIG.minRotation, CONFIG.maxRotation);
const duration = rand(CONFIG.minDuration, CONFIG.maxDuration);
const startOpacity = rand(0.7, 1);
el.style.left = startX + 'px';
el.style.opacity = startOpacity;
document.body.appendChild(el);
activeCount++;
const startTime = performance.now();
function animate(now) {
const elapsed = now - startTime;
const progress = elapsed / duration;
if (progress >= 1) {
el.remove();
activeCount--;
return;
}
const y = progress * (window.innerHeight + size + 120);
const x = Math.sin(progress * Math.PI * 2) * sway;
const rot = progress * rotation;
const opacity = progress > 0.8 ? startOpacity * (1 - (progress - 0.8) / 0.2) : startOpacity;
el.style.transform = `translate(${x}px, ${y}px) rotate(${rot}deg)`;
el.style.opacity = opacity;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
}
setInterval(spawnConfetti, CONFIG.spawnRate);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment