animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Cloudy Spiral CSS animation

model output

Claude Sonnet 4.6

Cloudy Spiral CSS animation

A 0.63T 0.34
compare with ground truth →
1<!DOCTYPE html>
2<html>
3<head>
4<style>
5* { margin: 0; padding: 0; box-sizing: border-box; }
6body { background: #3d6d9e; overflow: hidden; width: 100vw; height: 100vh; }
7canvas { display: block; }
8</style>
9</head>
10<body>
11<canvas id="c"></canvas>
12<script>
13const canvas = document.getElementById('c');
14const ctx = canvas.getContext('2d');
15canvas.width = window.innerWidth;
16canvas.height = window.innerHeight;
17
18const W = canvas.width, H = canvas.height;
19
20// Spiral parameters - center around 40% x, 45% y
21const spiralCX = W * 0.42;
22const spiralCY = H * 0.45;
23
24// Particle trail along a spiral path
25// The spiral: starts from bottom-right, loops around
26// Based on frames: loop center ~(420,380) in 1024x768 scale
27// Scale to our canvas
28const scaleX = W / 1024;
29const scaleY = H / 768;
30
31const cx = 420 * scaleX;
32const cy = 380 * scaleY;
33
34const NUM_DOTS = 36;
35const LOOP_DURATION = 3000; // ms for one full cycle
36
37// Bokeh orbs that sweep through
38const bokehOrbs = [
39  { baseX: -0.15, baseY: 0.85, r: 0.18, phase: 0 },
40  { baseX: 0.05, baseY: 0.65, r: 0.12, phase: 0 },
41  { baseX: 0.55, baseY: -0.05, r: 0.16, phase: 0 },
42  { baseX: 0.65, baseY: 0.1, r: 0.12, phase: 0 },
43];
44
45// The spiral path: parametric
46// From frames: dots form a loop (circle ~radius 80px at 1024 scale)
47// with a tail that spirals inward/outward
48function getSpiralPoint(t) {
49  // t from 0 to 1 represents position along the trail
50  // The head of the trail moves along a circular path
51  // Tail dots are smaller and follow behind
52  
53  // Main loop: circle of radius ~85px (scaled)
54  const loopR = 85 * scaleX;
55  // Tail extends downward-right from the loop
56  
57  // Parametric: for t in [0,1], 0=tail end, 1=head
58  // Head traces a circle, tail spirals out
59  
60  if (t > 0.45) {
61    // Loop portion
62    const lt = (t - 0.45) / 0.55;
63    const angle = Math.PI * 0.5 + lt * Math.PI * 2 * 0.85;
64    const r = loopR * (0.7 + 0.3 * lt);
65    return {
66      x: cx + Math.cos(angle) * r,
67      y: cy + Math.sin(angle) * r,
68      size: 3 * scaleX * (0.5 + 0.5 * lt)
69    };
70  } else {
71    // Tail portion - curves from bottom-right toward loop entry
72    const tt = t / 0.45;
73    // Tail goes from bottom-right corner area toward loop
74    const startX = cx + 180 * scaleX;
75    const startY = cy + 200 * scaleY;
76    const endX = cx + loopR * Math.cos(Math.PI * 0.5);
77    const endY = cy + loopR * Math.sin(Math.PI * 0.5);
78    // Bezier-like curve
79    const bx = cx + 80 * scaleX;
80    const by = cy + 160 * scaleY;
81    const x = (1-tt)*(1-tt)*startX + 2*(1-tt)*tt*bx + tt*tt*endX;
82    const y = (1-tt)*(1-tt)*startY + 2*(1-tt)*tt*by + tt*tt*endY;
83    const size = (8 + 20 * tt) * scaleX;
84    return { x, y, size };
85  }
86}
87
88// Bokeh sweep animation
89// Large orbs sweep from bottom-right to top-left and back
90function getBokehState(time) {
91  const period = 3000;
92  const t = (time % period) / period;
93  
94  // Sweep: orbs move from bottom-right off-screen to top-left off-screen
95  // Based on frames 6-8 and 10-12 showing large orbs sweeping
96  const sweep = Math.sin(t * Math.PI * 2);
97  
98  return {
99    sweep,
100    t,
101    visible: true
102  };
103}
104
105function drawBokeh(time) {
106  const period = 3000;
107  const t = (time % period) / period;
108  
109  // Two phases: sweep right-to-left (frames 7-8), then left-to-right (frames 10-11)
110  // Orbs appear at bottom-right, move to top-left, disappear, reappear top-right, move down
111  
112  // Phase 1: t=0.4 to 0.6 - large orbs sweep from bottom-right to left
113  // Phase 2: t=0.7 to 0.9 - orbs at top-right moving down
114  
115  const orbConfigs = [
116    { dx: 0.65, dy: 0.85, r: 0.18 * W },
117    { dx: 0.52, dy: 0.68, r: 0.12 * W },
118    { dx: 0.42, dy: 0.55, r: 0.09 * W },
119    { dx: 0.35, dy: 0.45, r: 0.07 * W },
120  ];
121  
122  // Sweep 1: bottom-right to left (t: 0.35 -> 0.65)
123  let alpha1 = 0;
124  let offsetX1 = 0, offsetY1 = 0;
125  if (t > 0.3 && t < 0.7) {
126    const st = (t - 0.3) / 0.4;
127    alpha1 = Math.sin(st * Math.PI) * 0.55;
128    offsetX1 = (st - 0.5) * -W * 1.2;
129    offsetY1 = (st - 0.5) * H * 0.3;
130  }
131  
132  // Sweep 2: top-right down (t: 0.7 -> 1.0)
133  let alpha2 = 0;
134  let offsetX2 = 0, offsetY2 = 0;
135  if (t > 0.65 && t < 1.0) {
136    const st = (t - 0.65) / 0.35;
137    alpha2 = Math.sin(st * Math.PI) * 0.45;
138    offsetX2 = W * 0.25 - st * W * 0.1;
139    offsetY2 = -H * 0.5 + st * H * 0.8;
140  }
141  
142  // Draw sweep 1 orbs
143  if (alpha1 > 0) {
144    orbConfigs.forEach((orb, i) => {
145      const x = orb.dx * W + offsetX1 - i * 80 * scaleX;
146      const y = orb.dy * H + offsetY1 - i * 60 * scaleY;
147      const grad = ctx.createRadialGradient(x, y, 0, x, y, orb.r);
148      grad.addColorStop(0, `rgba(220,235,255,${alpha1 * 0.9})`);
149      grad.addColorStop(0.5, `rgba(200,220,245,${alpha1 * 0.5})`);
150      grad.addColorStop(1, `rgba(180,210,240,0)`);
151      ctx.beginPath();
152      ctx.arc(x, y, orb.r, 0, Math.PI * 2);
153      ctx.fillStyle = grad;
154      ctx.fill();
155    });
156  }
157  
158  // Draw sweep 2 orbs (top-right)
159  if (alpha2 > 0) {
160    const orbs2 = [
161      { r: 0.16 * W },
162      { r: 0.11 * W },
163      { r: 0.08 * W },
164      { r: 0.06 * W },
165      { r: 0.04 * W },
166    ];
167    orbs2.forEach((orb, i) => {
168      const x = W * 0.72 + offsetX2 - i * 5 * scaleX;
169      const y = H * 0.1 + offsetY2 + i * 55 * scaleY;
170      const grad = ctx.createRadialGradient(x, y, 0, x, y, orb.r);
171      grad.addColorStop(0, `rgba(220,235,255,${alpha2 * 0.85})`);
172      grad.addColorStop(0.5, `rgba(200,220,245,${alpha2 * 0.45})`);
173      grad.addColorStop(1, `rgba(180,210,240,0)`);
174      ctx.beginPath();
175      ctx.arc(x, y, orb.r, 0, Math.PI * 2);
176      ctx.fillStyle = grad;
177      ctx.fill();
178    });
179  }
180}
181
182function drawGlow(x, y, r, alpha) {
183  const grad = ctx.createRadialGradient(x, y, 0, x, y, r * 3);
184  grad.addColorStop(0, `rgba(255,255,255,${alpha})`);
185  grad.addColorStop(0.3, `rgba(220,235,255,${alpha * 0.6})`);
186  grad.addColorStop(1, `rgba(180,210,240,0)`);
187  ctx.beginPath();
188  ctx.arc(x, y, r * 3, 0, Math.PI * 2);
189  ctx.fillStyle = grad;
190  ctx.fill();
191}
192
193let startTime = null;
194
195function animate(timestamp) {
196  if (!startTime) startTime = timestamp;
197  const elapsed = timestamp - startTime;
198  
199  // Clear
200  ctx.fillStyle = '#3d6d9e';
201  ctx.fillRect(0, 0, W, H);
202  
203  // Draw bokeh orbs
204  drawBokeh(elapsed);
205  
206  // Draw spiral dot trail
207  // Head position advances over time
208  const period = LOOP_DURATION;
209  const headT = (elapsed % period) / period;
210  
211  // Draw dots from tail to head
212  // The "head" dot is the brightest/largest
213  // Dots fade as they get older (further from head)
214  
215  // We show NUM_DOTS dots, each spaced along the path
216  // The head is at headT, tail is headT - (NUM_DOTS/totalDots)
217  
218  const totalPathDots = 60; // virtual dots on full path
219  
220  for (let i = 0; i < NUM_DOTS; i++) {
221    const dotFraction = i / (NUM_DOTS - 1);
222    // Position along path: head is newest
223    const pathT = ((headT - (NUM_DOTS - 1 - i) / totalPathDots) + 10) % 1;
224    
225    const pt = getSpiralPoint(pathT);
226    
227    // Brightness: head is brightest
228    const brightness = 0.3 + 0.7 * dotFraction;
229    const alpha = brightness * 0.9;
230    
231    // Draw dot with glow
232    const r = pt.size;
233    
234    // Glow
235    const grad = ctx.createRadialGradient(pt.x, pt.y, 0, pt.x, pt.y, r * 2.5);
236    grad.addColorStop(0, `rgba(255,255,255,${alpha})`);
237    grad.addColorStop(0.5, `rgba(220,235,255,${alpha * 0.5})`);
238    grad.addColorStop(1, `rgba(180,210,240,0)`);
239    ctx.beginPath();
240    ctx.arc(pt.x, pt.y, r * 2.5, 0, Math.PI * 2);
241    ctx.fillStyle = grad;
242    ctx.fill();
243    
244    // Core dot
245    ctx.beginPath();
246    ctx.arc(pt.x, pt.y, r, 0, Math.PI * 2);
247    ctx.fillStyle = `rgba(255,255,255,${alpha})`;
248    ctx.fill();
249  }
250  
251  requestAnimationFrame(animate);
252}
253
254requestAnimationFrame(animate);
255</script>
256</body>
257</html>