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

model output

Claude Sonnet 4.6

Only CSS: 3D Scan

A 0.76T 0.32
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>3D Cube with Spiral Rings</title>
7<style>
8  * { margin: 0; padding: 0; box-sizing: border-box; }
9  body {
10    background: radial-gradient(ellipse at center, #0a1628 0%, #050d1a 100%);
11    display: flex;
12    align-items: center;
13    justify-content: center;
14    width: 100vw;
15    height: 100vh;
16    overflow: hidden;
17  }
18  canvas { display: block; }
19</style>
20</head>
21<body>
22<canvas id="c"></canvas>
23<script>
24const canvas = document.getElementById('c');
25const ctx = canvas.getContext('2d');
26canvas.width = 1024;
27canvas.height = 768;
28
29const W = canvas.width, H = canvas.height;
30const cx = W * 0.5, cy = H * 0.5;
31
32// Isometric cube parameters
33const cubeSize = 160;
34const layers = 18;
35const layerStep = 7;
36
37// Isometric projection
38function iso(x, y, z) {
39  return {
40    x: cx + (x - z) * Math.cos(Math.PI / 6) * 1.0,
41    y: cy + (x + z) * Math.sin(Math.PI / 6) * 0.85 - y * 0.9
42  };
43}
44
45function drawCube(t) {
46  const s = cubeSize;
47  
48  for (let i = layers; i >= 0; i--) {
49    const offset = i * layerStep;
50    const alpha = 0.08 + (i / layers) * 0.12;
51    ctx.strokeStyle = `rgba(180, 210, 240, ${alpha})`;
52    ctx.lineWidth = 0.7;
53    
54    // Draw one cube outline layer
55    // Front face (y=0 plane, varying x,z)
56    const corners = [
57      iso(-s + offset, -s + offset*0.3, -s + offset),
58      iso( s - offset, -s + offset*0.3, -s + offset),
59      iso( s - offset,  s - offset*0.3, -s + offset),
60      iso(-s + offset,  s - offset*0.3, -s + offset),
61    ];
62    
63    // Top face
64    const top = [
65      iso(-s + offset, -s + offset*0.3, -s + offset),
66      iso( s - offset, -s + offset*0.3, -s + offset),
67      iso( s - offset, -s + offset*0.3,  s - offset),
68      iso(-s + offset, -s + offset*0.3,  s - offset),
69    ];
70    
71    // Right face
72    const right = [
73      iso( s - offset, -s + offset*0.3, -s + offset),
74      iso( s - offset, -s + offset*0.3,  s - offset),
75      iso( s - offset,  s - offset*0.3,  s - offset),
76      iso( s - offset,  s - offset*0.3, -s + offset),
77    ];
78    
79    [corners, top, right].forEach(face => {
80      ctx.beginPath();
81      ctx.moveTo(face[0].x, face[0].y);
82      for (let j = 1; j < face.length; j++) ctx.lineTo(face[j].x, face[j].y);
83      ctx.closePath();
84      ctx.stroke();
85    });
86  }
87}
88
89// Draw the back face glow
90function drawBackFaceGlow() {
91  const s = cubeSize - layers * layerStep;
92  const tl = iso(-s, -s*0.3, s);
93  const tr = iso( s, -s*0.3, s);
94  const br = iso( s,  s*0.3, s);
95  const bl = iso(-s,  s*0.3, s);
96  
97  ctx.beginPath();
98  ctx.moveTo(tl.x, tl.y);
99  ctx.lineTo(tr.x, tr.y);
100  ctx.lineTo(br.x, br.y);
101  ctx.lineTo(bl.x, bl.y);
102  ctx.closePath();
103  
104  const grad = ctx.createLinearGradient(tl.x, tl.y, br.x, br.y);
105  grad.addColorStop(0, 'rgba(20, 80, 120, 0.3)');
106  grad.addColorStop(1, 'rgba(10, 40, 80, 0.5)');
107  ctx.fillStyle = grad;
108  ctx.fill();
109}
110
111// Spiral rings animation
112function drawRings(t) {
113  const duration = 6; // seconds for full cycle
114  const phase = (t % duration) / duration; // 0 to 1
115  
116  // Animation phases:
117  // 0.0 - 0.5: rings are together, spiraling (contracting)
118  // 0.5 - 0.7: rings split into two groups
119  // 0.7 - 1.0: rings come back together
120  
121  const numRings = 12;
122  const maxRadius = 110;
123  const minRadius = 15;
124  
125  // Center of the face in screen coords
126  const faceCenter = iso(0, 0, cubeSize - layers * layerStep);
127  
128  let splitAmount = 0;
129  let ringScale = 1;
130  
131  if (phase < 0.5) {
132    // Contracting phase
133    ringScale = 1 - phase * 0.5;
134    splitAmount = 0;
135  } else if (phase < 0.7) {
136    // Split phase
137    const sp = (phase - 0.5) / 0.2;
138    ringScale = 0.75 - sp * 0.2;
139    splitAmount = sp;
140  } else {
141    // Rejoin and expand
142    const rp = (phase - 0.7) / 0.3;
143    ringScale = 0.55 + rp * 0.45;
144    splitAmount = 1 - rp;
145  }
146  
147  const rotAngle = t * 0.8;
148  
149  // Draw rings
150  for (let i = 0; i < numRings; i++) {
151    const frac = i / (numRings - 1);
152    const baseRadius = minRadius + frac * (maxRadius - minRadius);
153    const r = baseRadius * ringScale;
154    
155    // Color: gradient from cyan/teal to purple/blue
156    const colorT = frac + rotAngle * 0.1;
157    const hue1 = 170 + Math.sin(colorT) * 30; // teal
158    const hue2 = 240 + Math.sin(colorT + 1) * 20; // blue/purple
159    const hue = hue1 + (hue2 - hue1) * frac;
160    
161    // Determine if split
162    let offsetX = 0;
163    if (splitAmount > 0) {
164      // Two groups: even rings go left, odd go right
165      const dir = (i % 2 === 0) ? -1 : 1;
166      offsetX = dir * splitAmount * 60;
167    }
168    
169    // Ellipse to simulate 3D perspective on the face
170    const scaleY = 0.85;
171    
172    ctx.save();
173    ctx.translate(faceCenter.x + offsetX, faceCenter.y);
174    ctx.rotate(rotAngle * 0.3 + frac * 0.5);
175    
176    // Color based on position
177    let color;
178    if (frac < 0.3) {
179      color = `rgba(50, 220, 180, 0.8)`;
180    } else if (frac < 0.7) {
181      const blend = (frac - 0.3) / 0.4;
182      const r2 = Math.round(50 + blend * 80);
183      const g2 = Math.round(220 - blend * 120);
184      const b2 = Math.round(180 + blend * 75);
185      color = `rgba(${r2}, ${g2}, ${b2}, 0.8)`;
186    } else {
187      color = `rgba(100, 80, 255, 0.9)`;
188    }
189    
190    ctx.strokeStyle = color;
191    ctx.lineWidth = 1.5;
192    ctx.beginPath();
193    ctx.ellipse(0, 0, r, r * scaleY, 0, 0, Math.PI * 2);
194    ctx.stroke();
195    ctx.restore();
196  }
197}
198
199let startTime = null;
200
201function animate(ts) {
202  if (!startTime) startTime = ts;
203  const t = (ts - startTime) / 1000;
204  
205  ctx.clearRect(0, 0, W, H);
206  
207  // Background
208  const bg = ctx.createRadialGradient(cx, cy, 0, cx, cy, W * 0.7);
209  bg.addColorStop(0, '#0d2035');
210  bg.addColorStop(1, '#050d18');
211  ctx.fillStyle = bg;
212  ctx.fillRect(0, 0, W, H);
213  
214  drawBackFaceGlow();
215  drawRings(t);
216  drawCube(t);
217  
218  requestAnimationFrame(animate);
219}
220
221requestAnimationFrame(animate);
222</script>
223</body>
224</html>