← back to Snow (Pure CSS)compare with ground truth →
model output
Claude Sonnet 4.6
Snow (Pure CSS)
A 0.89T 0.16
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 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 background: #0a1520;
12 overflow: hidden;
13 width: 100vw;
14 height: 100vh;
15 }
16
17 canvas {
18 display: block;
19 width: 100%;
20 height: 100%;
21 }
22</style>
23</head>
24<body>
25<canvas id="c"></canvas>
26<script>
27const canvas = document.getElementById('c');
28const ctx = canvas.getContext('2d');
29
30canvas.width = window.innerWidth;
31canvas.height = window.innerHeight;
32
33const W = canvas.width;
34const H = canvas.height;
35
36// Background gradient
37function drawBackground() {
38 const grad = ctx.createRadialGradient(W*0.55, H*0.45, 0, W*0.55, H*0.45, W*0.7);
39 grad.addColorStop(0, '#1a3040');
40 grad.addColorStop(0.4, '#0f2030');
41 grad.addColorStop(1, '#060e18');
42 ctx.fillStyle = grad;
43 ctx.fillRect(0, 0, W, H);
44}
45
46// Star types: bright (white, glowing), medium (light gray), small (tiny white dots)
47const stars = [];
48const NUM_STARS = 120;
49
50function rand(min, max) { return Math.random() * (max - min) + min; }
51
52for (let i = 0; i < NUM_STARS; i++) {
53 const type = Math.random();
54 let size, brightness, blur, speed;
55
56 if (type < 0.25) {
57 // Bright stars - larger, white, glowing
58 size = rand(3, 6);
59 brightness = rand(0.85, 1.0);
60 blur = rand(2, 5);
61 speed = rand(0.15, 0.35);
62 } else if (type < 0.6) {
63 // Medium stars - gray, medium size
64 size = rand(3, 7);
65 brightness = rand(0.4, 0.65);
66 blur = rand(1, 3);
67 speed = rand(0.1, 0.25);
68 } else {
69 // Small dots - tiny
70 size = rand(1, 2.5);
71 brightness = rand(0.6, 0.9);
72 blur = 0;
73 speed = rand(0.05, 0.2);
74 }
75
76 stars.push({
77 x: rand(-50, W + 50),
78 y: rand(-50, H + 50),
79 size,
80 brightness,
81 blur,
82 speed,
83 // Drift direction - mostly upward and slightly sideways
84 dx: rand(-0.3, 0.3),
85 dy: rand(-speed, -speed * 0.3),
86 // Twinkle
87 twinkleSpeed: rand(0.005, 0.03),
88 twinklePhase: rand(0, Math.PI * 2),
89 twinkleAmp: rand(0.05, 0.25),
90 // Parallax layer
91 layer: Math.random()
92 });
93}
94
95function drawStar(star, time) {
96 const twinkle = 1 + Math.sin(time * star.twinkleSpeed * 60 + star.twinklePhase) * star.twinkleAmp;
97 const alpha = Math.min(1, star.brightness * twinkle);
98 const r = star.size * twinkle;
99
100 ctx.save();
101
102 if (star.blur > 0) {
103 ctx.shadowBlur = star.blur * 3;
104 ctx.shadowColor = `rgba(255,255,255,${alpha * 0.8})`;
105 }
106
107 // Bright stars get a glow
108 if (star.brightness > 0.8) {
109 const glow = ctx.createRadialGradient(star.x, star.y, 0, star.x, star.y, r * 3);
110 glow.addColorStop(0, `rgba(255,255,255,${alpha})`);
111 glow.addColorStop(0.3, `rgba(255,255,255,${alpha * 0.6})`);
112 glow.addColorStop(1, `rgba(255,255,255,0)`);
113 ctx.fillStyle = glow;
114 ctx.beginPath();
115 ctx.arc(star.x, star.y, r * 3, 0, Math.PI * 2);
116 ctx.fill();
117 }
118
119 // Core dot
120 const gray = Math.floor(star.brightness * 255);
121 ctx.fillStyle = `rgba(${gray},${gray},${gray + 10},${alpha})`;
122 ctx.beginPath();
123 ctx.arc(star.x, star.y, r, 0, Math.PI * 2);
124 ctx.fill();
125
126 ctx.restore();
127}
128
129let lastTime = 0;
130
131function animate(timestamp) {
132 const dt = timestamp - lastTime;
133 lastTime = timestamp;
134
135 drawBackground();
136
137 for (let s of stars) {
138 // Move star
139 const layerSpeed = 0.5 + s.layer * 0.5;
140 s.x += s.dx * layerSpeed;
141 s.y += s.dy * layerSpeed;
142
143 // Wrap around
144 if (s.y < -20) s.y = H + 20;
145 if (s.y > H + 20) s.y = -20;
146 if (s.x < -20) s.x = W + 20;
147 if (s.x > W + 20) s.x = -20;
148
149 drawStar(s, timestamp);
150 }
151
152 requestAnimationFrame(animate);
153}
154
155requestAnimationFrame(animate);
156
157window.addEventListener('resize', () => {
158 canvas.width = window.innerWidth;
159 canvas.height = window.innerHeight;
160});
161</script>
162</body>
163</html>