Created
January 2, 2026 21:37
-
-
Save FindMuck/d88a02d23f1f433f685c236c01a04cfb to your computer and use it in GitHub Desktop.
Reverse engineered 'Let it Snow' effect from the 2025 Holiday Season in Google AI Studio
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
| <!-- | |
| Reverse engineered 'Let it Snow' effect from the 2025 Holiday Season in Google AI Studio. | |
| All credit to Google AI Studio for the original design and logic. | |
| --> | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Google AI Studio Easter Egg (Source Accurate)</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| background-color: #1e1e1e; /* Dark theme background */ | |
| height: 100vh; | |
| width: 100vw; | |
| overflow: hidden; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| /* | |
| ORIGINAL CSS (Found in hkd.va metadata): | |
| The 'opacity: .5' rule is the most important part. | |
| It ensures the snow looks like a background effect rather than harsh static. | |
| */ | |
| canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| opacity: 0.5; | |
| pointer-events: none; | |
| z-index: 1000; | |
| } | |
| /* Button for interaction (Standard UI to trigger the effect) */ | |
| button { | |
| position: relative; | |
| z-index: 1001; | |
| padding: 12px 24px; | |
| font-family: sans-serif; | |
| cursor: pointer; | |
| background: rgba(255, 255, 255, 0.1); | |
| color: #e3e3e3; | |
| border: 1px solid #555; | |
| border-radius: 8px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <canvas id="snowCanvas"></canvas> | |
| <button id="toggleSnow">Let it snow</button> | |
| <script> | |
| /** | |
| * Logic extracted strictly from class 'hkd' in the source dump. | |
| */ | |
| class SnowEffect { | |
| constructor(canvasElement) { | |
| this.canvas = canvasElement; | |
| this.ctx = this.canvas.getContext('2d'); | |
| // Source: "this.aa = []" | |
| this.particles = []; | |
| // Source: "this.J = 0" (Last timestamp) | |
| this.lastTime = 0; | |
| // Source: "this.running = !1" | |
| this.running = false; | |
| // Source: "this.F = 0" (Target Opacity) | |
| this.targetOpacity = 0; | |
| // Source: "this.opacity = 0" (Current Opacity) | |
| this.currentOpacity = 0; | |
| // Source: "this.Rd = new ResizeObserver..." | |
| // Handles High-DPI (Retina) displays logic | |
| const resizeObserver = new ResizeObserver(() => { | |
| this.canvas.width = window.innerWidth * window.devicePixelRatio; | |
| this.canvas.height = window.innerHeight * window.devicePixelRatio; | |
| if(this.running) this.render(); | |
| }); | |
| resizeObserver.observe(document.body); | |
| // Source Loop: "for (let a = 0; a < 2E3; a++)" | |
| // Exactly 2000 particles. | |
| for (let i = 0; i < 2E3; i++) { | |
| this.particles.push({ | |
| x: Math.random(), | |
| y: Math.random(), | |
| // Source: "vx: Math.random() - .5" | |
| // Range [-0.5, 0.5]. No wind bias. | |
| vx: Math.random() - 0.5, | |
| // Source: "vy: (1 + Math.random() * 10) / 10" | |
| // Range [0.1, 1.1]. Gravity factor. | |
| vy: (1 + Math.random() * 10) / 10, | |
| // Source: "freqx: 1 + Math.random() * 5" | |
| freqx: 1 + Math.random() * 5, | |
| freqy: 1 + Math.random() * 5, | |
| // Source: "size: .1 + Math.random() * 1.4" | |
| // Range [0.1, 1.5]. | |
| // Note: Browsers render <1px sizes as semi-transparent "ghosts". | |
| size: 0.1 + Math.random() * 1.4, | |
| phasex: Math.random() * 2 * Math.PI, | |
| phasey: Math.random() * 2 * Math.PI | |
| }); | |
| } | |
| } | |
| // Logic from "Osa" function (Toggle state) | |
| toggle() { | |
| if (this.targetOpacity === 0) { | |
| this.targetOpacity = 1; // F = 1 | |
| if (!this.running) { | |
| this.lastTime = performance.now(); | |
| this.running = true; | |
| this.animate(); | |
| } | |
| } else { | |
| this.targetOpacity = 0; // F = 0 | |
| } | |
| } | |
| animate() { | |
| requestAnimationFrame(() => { | |
| this.render(); | |
| // Stop rendering only when fully invisible | |
| if (Math.abs(this.currentOpacity) > 1e-6 || this.targetOpacity !== 0) { | |
| this.animate(); | |
| } else { | |
| this.running = false; | |
| this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| } | |
| }); | |
| } | |
| // Logic from "render" function | |
| render() { | |
| // Fade Logic: "this.opacity += (this.F - this.opacity) * .05" | |
| this.currentOpacity += (this.targetOpacity - this.currentOpacity) * 0.05; | |
| const w = this.canvas.width; | |
| const h = this.canvas.height; | |
| // Time logic: "d = performance.now()" | |
| const now = performance.now(); | |
| // Delta logic: "e = (d - this.J) / 16" | |
| const delta = (now - this.lastTime) / 16; | |
| // Apply calculated opacity to context | |
| this.ctx.globalAlpha = this.currentOpacity; | |
| this.ctx.clearRect(0, 0, w, h); | |
| // Color Logic: "this.fa.cn() ? 'white' : 'lightskyblue'" | |
| // We assume Dark Mode is active for this demo. | |
| this.ctx.fillStyle = "white"; | |
| for (const p of this.particles) { | |
| // --- INVERSE PHYSICS --- | |
| // Source: "k = 2 * f.vx / f.size / b" | |
| // Velocity is divided by size. Small particles move FAST. | |
| const k = 2 * p.vx / p.size / w; | |
| // Source: "l = 2 * f.vy / f.size / c" | |
| const l = 2 * p.vy / p.size / h; | |
| const drawX = p.x * w; | |
| const drawY = p.y * h; | |
| this.ctx.beginPath(); | |
| // --- SWAY MATH --- | |
| // Source: "g + b / 200 * Math.sin(f.freqx * d * l + f.phasex)" | |
| const swayX = w / 200 * Math.sin(p.freqx * now * l + p.phasex); | |
| // Source: "h + c / 200 * Math.sin(f.freqy * d * k + f.phasey)" | |
| const swayY = h / 200 * Math.sin(p.freqy * now * k + p.phasey); | |
| this.ctx.arc( | |
| drawX + swayX, | |
| drawY + swayY, | |
| p.size * window.devicePixelRatio, | |
| 0, | |
| 2 * Math.PI | |
| ); | |
| this.ctx.fill(); | |
| // --- POSITION UPDATES --- | |
| // Source: "f.x += k * e" | |
| p.x += k * delta; | |
| p.y += l * delta; | |
| // Source: "f.x %= 1" (Wrap around) | |
| p.x %= 1; | |
| p.y %= 1; | |
| // Handle negative wrap (JS modulo allows negatives) | |
| if (p.x < 0) p.x += 1; | |
| if (p.y < 0) p.y += 1; | |
| } | |
| this.lastTime = now; | |
| } | |
| } | |
| const snow = new SnowEffect(document.getElementById('snowCanvas')); | |
| document.getElementById('toggleSnow').addEventListener('click', () => snow.toggle()); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment