animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Only CSS: Planet Ring

model output

Claude Sonnet 4.6

Only CSS: Planet Ring

A 0.67T 0.31
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>Vinyl Record Animation</title>
7<style>
8  * { margin: 0; padding: 0; box-sizing: border-box; }
9  body {
10    background: #e8eaf0;
11    display: flex;
12    justify-content: center;
13    align-items: center;
14    width: 100vw;
15    height: 100vh;
16    overflow: hidden;
17  }
18  canvas {
19    display: block;
20  }
21</style>
22</head>
23<body>
24<canvas id="c"></canvas>
25<script>
26const canvas = document.getElementById('c');
27const ctx = canvas.getContext('2d');
28canvas.width = 600;
29canvas.height = 600;
30
31const cx = 300, cy = 300;
32const R = 220; // outer radius of record
33const innerR = 90; // inner white area radius
34const centerR = 55; // orange center radius
35const numRings = 14;
36
37let time = 0;
38const duration = 4000; // ms per full cycle
39let startTime = null;
40
41function easeInOut(t) {
42  return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
43}
44
45function drawRecord(angle) {
46  // angle: rotation angle in radians for the 3D tilt effect
47  // We simulate 3D rotation around Y axis by squishing X
48  // and around X axis by squishing Y
49  
50  ctx.clearRect(0, 0, canvas.width, canvas.height);
51  
52  // Background
53  ctx.fillStyle = '#e8eaf0';
54  ctx.fillRect(0, 0, canvas.width, canvas.height);
55  
56  ctx.save();
57  ctx.translate(cx, cy);
58  
59  // The record rotates in 3D - we simulate with CSS-like perspective
60  // angle goes from 0 to 2*PI continuously
61  // scaleX = cos(angle), scaleY = 1 (rotation around Y axis)
62  // But looking at frames, it seems to rotate around a tilted axis
63  
64  const rotY = angle; // rotation around Y axis
65  const tiltAngle = Math.PI / 4; // 45 degree tilt of rotation axis
66  
67  // Compute the apparent scale factors
68  const scaleX = Math.cos(rotY);
69  const skewFactor = Math.sin(rotY) * Math.sin(tiltAngle);
70  
71  // Draw rings
72  for (let i = numRings; i >= 0; i--) {
73    const t = i / numRings;
74    const r = innerR + (R - innerR) * t;
75    
76    drawEllipse(r, rotY, tiltAngle, i);
77  }
78  
79  // Draw inner white circle area
80  drawFilledEllipse(innerR, rotY, tiltAngle, '#dde0ea');
81  
82  // Draw center orange circle
83  drawCenterCircle(centerR, rotY, tiltAngle);
84  
85  ctx.restore();
86}
87
88function getEllipseParams(r, rotY, tiltAngle) {
89  // Simulate 3D rotation: the circle of radius r appears as an ellipse
90  // when rotated around an axis
91  // For rotation around Y axis by rotY:
92  // x' = x * cos(rotY)
93  // y' = y
94  // But with tilt, the axis is tilted by tiltAngle
95  
96  const cosY = Math.cos(rotY);
97  const sinY = Math.sin(rotY);
98  
99  // The ellipse parameters after rotation
100  // When rotating around Y axis: rx = r * |cos(rotY)|, ry = r
101  // With tilt angle applied as a rotation of the whole thing
102  
103  return {
104    rx: r * Math.abs(cosY),
105    ry: r,
106    rotation: tiltAngle * sinY * 0.5, // slight rotation based on phase
107    scaleSign: Math.sign(cosY) || 1
108  };
109}
110
111function drawEllipse(r, rotY, tiltAngle, ringIndex) {
112  const cosY = Math.cos(rotY);
113  const sinY = Math.sin(rotY);
114  
115  // Effective radii
116  const rx = r * Math.abs(cosY);
117  const ry = r;
118  
119  // Overall rotation of the ellipse shape
120  const rot = tiltAngle;
121  
122  ctx.save();
123  ctx.rotate(rot);
124  
125  const alpha = 0.7 + 0.3 * (ringIndex / numRings);
126  ctx.strokeStyle = `rgba(70, 75, 85, ${alpha})`;
127  ctx.lineWidth = 1.5;
128  
129  ctx.beginPath();
130  ctx.ellipse(0, 0, Math.max(rx, 1), ry, 0, 0, Math.PI * 2);
131  ctx.stroke();
132  
133  ctx.restore();
134}
135
136function drawFilledEllipse(r, rotY, tiltAngle, color) {
137  const cosY = Math.cos(rotY);
138  const rx = r * Math.abs(cosY);
139  const ry = r;
140  const rot = tiltAngle;
141  
142  ctx.save();
143  ctx.rotate(rot);
144  ctx.fillStyle = color;
145  ctx.beginPath();
146  ctx.ellipse(0, 0, Math.max(rx, 1), ry, 0, 0, Math.PI * 2);
147  ctx.fill();
148  ctx.restore();
149}
150
151function drawCenterCircle(r, rotY, tiltAngle) {
152  const cosY = Math.cos(rotY);
153  const rx = r * Math.abs(cosY);
154  const ry = r;
155  const rot = tiltAngle;
156  
157  ctx.save();
158  ctx.rotate(rot);
159  
160  // Black border
161  ctx.beginPath();
162  ctx.ellipse(0, 0, Math.max(rx + 6, 6), ry + 6, 0, 0, Math.PI * 2);
163  ctx.fillStyle = '#111';
164  ctx.fill();
165  
166  // Orange gradient fill
167  const grad = ctx.createRadialGradient(-rx * 0.2, -ry * 0.2, 0, 0, 0, ry);
168  grad.addColorStop(0, '#ff7043');
169  grad.addColorStop(0.6, '#ff5722');
170  grad.addColorStop(1, '#e64a19');
171  
172  ctx.beginPath();
173  ctx.ellipse(0, 0, Math.max(rx, 1), ry, 0, 0, Math.PI * 2);
174  ctx.fillStyle = grad;
175  ctx.fill();
176  
177  // Highlight
178  ctx.beginPath();
179  ctx.ellipse(-rx * 0.15, -ry * 0.2, Math.max(rx * 0.4, 1), ry * 0.35, 0, 0, Math.PI * 2);
180  ctx.fillStyle = 'rgba(255, 180, 120, 0.3)';
181  ctx.fill();
182  
183  ctx.restore();
184}
185
186// Better approach: draw the vinyl record with proper 3D rotation simulation
187function drawVinyl(t) {
188  ctx.clearRect(0, 0, canvas.width, canvas.height);
189  ctx.fillStyle = '#e8eaf0';
190  ctx.fillRect(0, 0, canvas.width, canvas.height);
191  
192  ctx.save();
193  ctx.translate(cx, cy);
194  
195  // t goes 0 to 1 for full animation cycle
196  // The record appears to spin in 3D
197  // Looking at frames: starts flat (circle), tilts to vertical ellipse, 
198  // then rotates through various orientations
199  
200  const angle = t * Math.PI * 2; // full rotation
201  
202  // The rotation axis appears tilted - let's use a combination
203  // rotY controls the squish in one direction
204  // The whole thing is also rotated by some angle
205  
206  const cosA = Math.cos(angle);
207  const sinA = Math.sin(angle);
208  
209  // Tilt of the rotation axis (45 degrees based on frames)
210  const axisAngle = Math.PI / 4;
211  
212  // Project: a circle rotating around a tilted axis
213  // The apparent shape is an ellipse with:
214  // - major axis = R (always)
215  // - minor axis = R * |cos(angle)|
216  // - orientation = axisAngle + some offset based on sin(angle)
217  
218  const minorScale = Math.abs(cosA);
219  
220  // The orientation of the ellipse rotates as the record spins
221  // When cosA > 0, orientation is axisAngle
222  // When cosA < 0, orientation flips
223  const ellipseRot = axisAngle + (cosA < 0 ? Math.PI / 2 * Math.abs(sinA) : 0);
224  
225  // Actually let's think more carefully:
226  // A disk rotating around Y axis (tilted 45deg in world space):
227  // The normal to the disk traces a cone
228  // The apparent ellipse: 
229  //   - one axis stays at R
230  //   - other axis = R * cos(angle)
231  //   - the whole ellipse is rotated by axisAngle in screen space
232  
233  const finalRot = axisAngle;
234  
235  ctx.rotate(finalRot);
236  
237  // Draw rings from outside in
238  for (let i = numRings; i >= 1; i--) {
239    const frac = i / numRings;
240    const r = innerR + (R - innerR) * frac;
241    const rx = r * minorScale;
242    const ry = r;
243    
244    const ringAlpha = 0.5 + 0.5 * frac;
245    ctx.strokeStyle = `rgba(65, 70, 80, ${ringAlpha})`;
246    ctx.lineWidth = 1.8;
247    ctx.beginPath();
248    ctx.ellipse(0, 0, Math.max(rx, 0.5), ry, 0, 0, Math.PI * 2);
249    ctx.stroke();
250  }
251  
252  // Inner white/light area
253  {
254    const rx = innerR * minorScale;
255    const ry = innerR;
256    ctx.fillStyle = '#dde0ea';
257    ctx.beginPath();
258    ctx.ellipse(0, 0, Math.max(rx, 0.5), ry, 0, 0, Math.PI * 2);
259    ctx.fill();
260  }
261  
262  // Center circle - always appears circular (sphere-like)
263  // Black border
264  ctx.beginPath();
265  ctx.arc(0, 0, centerR + 6, 0, Math.PI * 2);
266  ctx.fillStyle = '#111';
267  ctx.fill();
268  
269  // Orange
270  const grad = ctx.createRadialGradient(-10, -12, 2, 0, 0, centerR);
271  grad.addColorStop(0, '#ff8a65');
272  grad.addColorStop(0.5, '#ff5722');
273  grad.addColorStop(1, '#d84315');
274  ctx.beginPath();
275  ctx.arc(0, 0, centerR, 0, Math.PI * 2);
276  ctx.fillStyle = grad;
277  ctx.fill();
278  
279  // Highlight
280  ctx.beginPath();
281  ctx.arc(-8, -10, centerR * 0.45, 0, Math.PI * 2);
282  ctx.fillStyle = 'rgba(255, 200, 150, 0.25)';
283  ctx.fill();
284  
285  ctx.restore();
286}
287
288function animate(timestamp) {
289  if (!startTime) startTime = timestamp;
290  const elapsed = (timestamp - startTime) % duration;
291  const t = elapsed / duration;
292  
293  drawVinyl(t);
294  
295  requestAnimationFrame(animate);
296}
297
298requestAnimationFrame(animate);
299</script>
300</body>
301</html>