← back to Snow (Pure CSS)compare with ground truth →
model output
Gemini 3 Flash Preview
Snow (Pure CSS)
A 0.55T 0.09
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Starfield Animation</title>
7 <style>
8 /* Base setup to ensure the canvas takes up the full screen */
9 body, html {
10 margin: 0;
11 padding: 0;
12 width: 100%;
13 height: 100%;
14 overflow: hidden;
15 background-color: #05070a; /* Darkest base color */
16 }
17
18 /* Gradient background matching the video's subtle deep navy glow */
19 #universe {
20 position: fixed;
21 width: 100%;
22 height: 100%;
23 background: radial-gradient(circle at 50% 50%, #101624 0%, #05070a 100%);
24 overflow: hidden;
25 }
26
27 /* Basic star styling */
28 .star {
29 position: absolute;
30 background-color: #ffffff;
31 border-radius: 50%;
32 pointer-events: none;
33 will-change: transform, opacity;
34 }
35
36 /*
37 Slow downward drift animation.
38 Starts slightly above the viewport and ends below it.
39 */
40 @keyframes drift {
41 from {
42 transform: translateY(-10vh);
43 }
44 to {
45 transform: translateY(110vh);
46 }
47 }
48
49 /*
50 Subtle twinkling animation.
51 Varies opacity and scale slightly for a shimmering effect.
52 */
53 @keyframes twinkle {
54 0%, 100% {
55 opacity: 0.3;
56 transform: scale(0.85) translateY(var(--drift-val, 0));
57 }
58 50% {
59 opacity: 1;
60 transform: scale(1) translateY(var(--drift-val, 0));
61 }
62 }
63
64 /*
65 Since combined animations on one element can be tricky with transforms,
66 we use nested containers or simple multi-animation.
67 In this case, we'll apply the vertical drift to the star and handle
68 opacity separately.
69 */
70 </style>
71</head>
72<body>
73
74 <div id="universe"></div>
75
76 <script>
77 const universe = document.getElementById('universe');
78 const starCount = 280; // Density matches the provided video frames
79
80 /**
81 * Generates a star with random properties to recreate the variety
82 * seen in the starfield (size, speed, depth).
83 */
84 function createStar() {
85 const star = document.createElement('div');
86 star.className = 'star';
87
88 // Variation in size (mostly tiny dots, a few larger ones)
89 const sizeRandomizer = Math.random();
90 let size = 0.8; // default tiny
91 if (sizeRandomizer > 0.95) {
92 size = Math.random() * 1.5 + 1.5; // Larger stars
93 } else if (sizeRandomizer > 0.7) {
94 size = Math.random() * 1 + 1; // Medium stars
95 } else {
96 size = Math.random() * 0.5 + 0.5; // Tiny stars
97 }
98
99 // Random horizontal placement
100 const posX = Math.random() * 100;
101
102 // Random duration for parallax effect (larger stars move faster)
103 // Range: 40s to 120s for a very slow, majestic drift
104 const duration = (Math.random() * 60 + 60) / (size * 0.5 + 0.5);
105
106 // Random start point (negative delay) so stars are pre-distributed
107 const delay = Math.random() * -duration;
108
109 // Random twinkling frequency
110 const twinkleDuration = Math.random() * 4 + 2;
111 const twinkleDelay = Math.random() * -5;
112
113 // Apply styles
114 star.style.width = `${size}px`;
115 star.style.height = `${size}px`;
116 star.style.left = `${posX}%`;
117 star.style.top = `0`; // Start at top, translateY handles motion
118
119 // Add subtle bloom/glow to larger stars
120 if (size > 1.8) {
121 star.style.boxShadow = `0 0 ${size * 2}px rgba(255, 255, 255, 0.6)`;
122 }
123
124 // Apply animations
125 // Note: twinkle affects opacity, drift affects translateY
126 star.style.animation = `
127 drift ${duration}s linear infinite,
128 twinkle ${twinkleDuration}s ease-in-out infinite alternate
129 `;
130 star.style.animationDelay = `${delay}s, ${twinkleDelay}s`;
131
132 universe.appendChild(star);
133 }
134
135 // Initialize the starfield
136 for (let i = 0; i < starCount; i++) {
137 createStar();
138 }
139
140 // Optimization: Redraw if window is resized (stars are % based so mostly fine)
141 window.addEventListener('resize', () => {
142 // Percent-based layout handles resize automatically
143 });
144 </script>
145</body>
146</html>