animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Wavy Loading Animations

model output

Claude Sonnet 4.6

Wavy Loading Animations

A 0.84T 0.25
compare with ground truth →
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>Loading Animation</title>
7<style>
8  * { margin: 0; padding: 0; box-sizing: border-box; }
9  
10  body {
11    background-color: #ebebeb;
12    display: flex;
13    justify-content: center;
14    align-items: flex-start;
15    min-height: 100vh;
16    padding-top: 65px;
17  }
18
19  .loader {
20    position: relative;
21    display: flex;
22    align-items: center;
23    height: 30px;
24  }
25
26  canvas {
27    display: block;
28  }
29</style>
30</head>
31<body>
32<canvas id="c" width="120" height="30"></canvas>
33
34<script>
35const canvas = document.getElementById('c');
36const ctx = canvas.getContext('2d');
37
38const W = canvas.width;
39const H = canvas.height;
40
41// Animation states:
42// 0: dashes (thin horizontal lines)
43// 1: perspective bars (tapered, growing left to right)
44// 2: equal bars (barcode style)
45// Then back to 0
46
47// Timing: each state ~400ms, transition between them
48// Looking at frames: dash -> perspective -> equal -> dash...
49
50let startTime = null;
51const cycleDuration = 1200; // ms per full cycle
52
53function drawDashes(alpha) {
54  // 10 dashes, thin, horizontal, centered
55  ctx.save();
56  ctx.globalAlpha = alpha;
57  const dashW = 5;
58  const dashH = 1.5;
59  const gap = 2;
60  const count = 10;
61  const totalW = count * dashW + (count - 1) * gap;
62  const startX = (W - totalW) / 2;
63  const y = H / 2 - dashH / 2;
64  ctx.fillStyle = '#555';
65  for (let i = 0; i < count; i++) {
66    ctx.fillRect(startX + i * (dashW + gap), y, dashW, dashH);
67  }
68  ctx.restore();
69}
70
71function drawPerspectiveBars(alpha) {
72  // Bars that grow in height from left to right (perspective effect)
73  // leftmost bar is very short, rightmost is tallest
74  ctx.save();
75  ctx.globalAlpha = alpha;
76  const count = 10;
77  const barW = 4;
78  const gap = 2;
79  const totalW = count * barW + (count - 1) * gap;
80  const startX = (W - totalW) / 2;
81  const maxH = 22;
82  const minH = 3;
83  ctx.fillStyle = '#888';
84  for (let i = 0; i < count; i++) {
85    const t = i / (count - 1);
86    const bh = minH + (maxH - minH) * t;
87    const x = startX + i * (barW + gap);
88    const y = H / 2 - bh / 2;
89    ctx.fillRect(x, y, barW, bh);
90  }
91  ctx.restore();
92}
93
94function drawEqualBars(alpha) {
95  // Equal height bars, barcode style
96  ctx.save();
97  ctx.globalAlpha = alpha;
98  const count = 10;
99  const barW = 4;
100  const gap = 2;
101  const totalW = count * barW + (count - 1) * gap;
102  const startX = (W - totalW) / 2;
103  const bh = 22;
104  const y = H / 2 - bh / 2;
105  ctx.fillStyle = '#888';
106  for (let i = 0; i < count; i++) {
107    const x = startX + i * (barW + gap);
108    ctx.fillRect(x, y, barW, bh);
109  }
110  ctx.restore();
111}
112
113// Interpolate between states
114function lerp(a, b, t) {
115  return a + (b - a) * t;
116}
117
118function drawInterpolated(t, fromState, toState) {
119  // t: 0 to 1 transition from fromState to toState
120  const count = 10;
121  const barW = 4;
122  const gap = 2;
123  const totalW = count * barW + (count - 1) * gap;
124  const startX = (W - totalW) / 2;
125  const maxH = 22;
126  const minH = 3;
127  const dashH = 1.5;
128
129  function getHeight(state, i) {
130    if (state === 0) return dashH; // dashes
131    if (state === 1) {
132      // perspective
133      const frac = i / (count - 1);
134      return minH + (maxH - minH) * frac;
135    }
136    if (state === 2) return maxH; // equal bars
137    return dashH;
138  }
139
140  function getColor(state) {
141    if (state === 0) return [85, 85, 85];
142    if (state === 1) return [136, 136, 136];
143    if (state === 2) return [136, 136, 136];
144    return [85, 85, 85];
145  }
146
147  const c1 = getColor(fromState);
148  const c2 = getColor(toState);
149  const r = Math.round(lerp(c1[0], c2[0], t));
150  const g = Math.round(lerp(c1[1], c2[1], t));
151  const b = Math.round(lerp(c1[2], c2[2], t));
152
153  ctx.fillStyle = `rgb(${r},${g},${b})`;
154
155  for (let i = 0; i < count; i++) {
156    const h1 = getHeight(fromState, i);
157    const h2 = getHeight(toState, i);
158    const bh = lerp(h1, h2, t);
159    const x = startX + i * (barW + gap);
160    const y = H / 2 - bh / 2;
161    ctx.fillRect(x, y, barW, bh);
162  }
163}
164
165function easeInOut(t) {
166  return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
167}
168
169function animate(timestamp) {
170  if (!startTime) startTime = timestamp;
171  const elapsed = (timestamp - startTime) % cycleDuration;
172
173  ctx.clearRect(0, 0, W, H);
174
175  // Cycle: 
176  // 0-200ms: hold dashes
177  // 200-600ms: transition dashes -> perspective
178  // 600-800ms: hold perspective  (actually looking at frames it goes dash->perspective->equal->dash)
179  // Actually re-examining: 
180  // Frame 1: dashes
181  // Frame 2: equal bars (barcode)
182  // Frame 3: perspective bars
183  // Frame 4: dashes again
184  // Frame 5: equal bars
185  // Frame 6: perspective
186  // Frame 7: dashes
187  // So cycle: dashes -> equal -> perspective -> dashes
188  // Each phase ~400ms
189
190  const phase = cycleDuration / 3; // 400ms each
191
192  if (elapsed < phase) {
193    // dashes -> equal bars
194    const t = easeInOut(elapsed / phase);
195    drawInterpolated(t, 0, 2);
196  } else if (elapsed < phase * 2) {
197    // equal bars -> perspective
198    const t = easeInOut((elapsed - phase) / phase);
199    drawInterpolated(t, 2, 1);
200  } else {
201    // perspective -> dashes
202    const t = easeInOut((elapsed - phase * 2) / phase);
203    drawInterpolated(t, 1, 0);
204  }
205
206  requestAnimationFrame(animate);
207}
208
209requestAnimationFrame(animate);
210</script>
211</body>
212</html>