animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Only CSS: Codevember #5 Lightsaber

model output

Claude Sonnet 4.6

Only CSS: Codevember #5 Lightsaber

A 0.77T 0.22
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>Lightsaber Animation</title>
7<style>
8  * { margin: 0; padding: 0; box-sizing: border-box; }
9  
10  body {
11    background: #000;
12    width: 100vw;
13    height: 100vh;
14    overflow: hidden;
15    display: flex;
16    align-items: center;
17    justify-content: center;
18  }
19
20  .scene {
21    position: relative;
22    width: 1024px;
23    height: 768px;
24  }
25
26  .lightsaber {
27    position: absolute;
28    left: 50%;
29    top: 50%;
30    transform-origin: bottom center;
31    animation: saberSwing 8s ease-in-out infinite;
32  }
33
34  .hilt {
35    position: absolute;
36    bottom: 0;
37    left: 50%;
38    transform: translateX(-50%);
39    width: 5px;
40    height: 20px;
41    background: linear-gradient(to bottom, #ddd, #aaa, #888);
42    border-radius: 2px;
43    z-index: 2;
44  }
45
46  .blade {
47    position: absolute;
48    bottom: 18px;
49    left: 50%;
50    transform: translateX(-50%);
51    width: 3px;
52    border-radius: 2px 2px 0 0;
53    animation: bladeColor 8s ease-in-out infinite, bladeGlow 8s ease-in-out infinite, bladeLength 8s ease-in-out infinite;
54  }
55
56  .glow {
57    position: absolute;
58    bottom: 0;
59    left: 50%;
60    transform: translateX(-50%);
61    border-radius: 50%;
62    animation: glowColor 8s ease-in-out infinite, glowPulse 8s ease-in-out infinite;
63    pointer-events: none;
64  }
65
66  @keyframes saberSwing {
67    0%   { transform: translate(-50px, -180px) rotate(-5deg); }
68    12%  { transform: translate(-50px, -180px) rotate(130deg); }
69    25%  { transform: translate(-50px, -180px) rotate(135deg); }
70    37%  { transform: translate(-50px, -180px) rotate(-10deg); }
71    50%  { transform: translate(-50px, -180px) rotate(-5deg); }
72    62%  { transform: translate(-50px, -180px) rotate(130deg); }
73    75%  { transform: translate(-50px, -180px) rotate(135deg); }
74    87%  { transform: translate(-50px, -180px) rotate(-10deg); }
75    100% { transform: translate(-50px, -180px) rotate(-5deg); }
76  }
77
78  @keyframes bladeLength {
79    0%   { height: 160px; }
80    5%   { height: 10px; }
81    10%  { height: 160px; }
82    50%  { height: 160px; }
83    55%  { height: 10px; }
84    60%  { height: 160px; }
85    100% { height: 160px; }
86  }
87
88  @keyframes bladeColor {
89    0%   { background: linear-gradient(to top, #4488ff, #88aaff, #ffffff); }
90    40%  { background: linear-gradient(to top, #4488ff, #88aaff, #ffffff); }
91    45%  { background: linear-gradient(to top, #00ffcc, #88ffee, #ffffff); }
92    48%  { background: linear-gradient(to top, #00ff44, #88ff88, #ffffff); }
93    50%  { background: linear-gradient(to top, #00ff44, #88ff88, #ffffff); }
94    90%  { background: linear-gradient(to top, #00ff44, #88ff88, #ffffff); }
95    95%  { background: linear-gradient(to top, #ff2200, #ff6644, #ffffff); }
96    100% { background: linear-gradient(to top, #ff2200, #ff6644, #ffffff); }
97  }
98
99  @keyframes bladeGlow {
100    0%   { box-shadow: 0 0 8px 4px rgba(68,136,255,0.8), 0 0 20px 8px rgba(68,136,255,0.4); }
101    40%  { box-shadow: 0 0 8px 4px rgba(68,136,255,0.8), 0 0 20px 8px rgba(68,136,255,0.4); }
102    45%  { box-shadow: 0 0 8px 4px rgba(0,255,200,0.8), 0 0 20px 8px rgba(0,255,200,0.4); }
103    48%  { box-shadow: 0 0 8px 4px rgba(0,255,68,0.8), 0 0 20px 8px rgba(0,255,68,0.4); }
104    50%  { box-shadow: 0 0 8px 4px rgba(0,255,68,0.8), 0 0 20px 8px rgba(0,255,68,0.4); }
105    90%  { box-shadow: 0 0 8px 4px rgba(0,255,68,0.8), 0 0 20px 8px rgba(0,255,68,0.4); }
106    95%  { box-shadow: 0 0 8px 4px rgba(255,34,0,0.8), 0 0 20px 8px rgba(255,34,0,0.4); }
107    100% { box-shadow: 0 0 8px 4px rgba(255,34,0,0.8), 0 0 20px 8px rgba(255,34,0,0.4); }
108  }
109
110  @keyframes glowColor {
111    0%   { background: radial-gradient(ellipse, rgba(68,136,255,0.3) 0%, transparent 70%); }
112    40%  { background: radial-gradient(ellipse, rgba(68,136,255,0.3) 0%, transparent 70%); }
113    45%  { background: radial-gradient(ellipse, rgba(0,255,200,0.3) 0%, transparent 70%); }
114    48%  { background: radial-gradient(ellipse, rgba(0,255,68,0.3) 0%, transparent 70%); }
115    50%  { background: radial-gradient(ellipse, rgba(0,255,68,0.3) 0%, transparent 70%); }
116    90%  { background: radial-gradient(ellipse, rgba(0,255,68,0.3) 0%, transparent 70%); }
117    95%  { background: radial-gradient(ellipse, rgba(255,34,0,0.3) 0%, transparent 70%); }
118    100% { background: radial-gradient(ellipse, rgba(255,34,0,0.3) 0%, transparent 70%); }
119  }
120
121  @keyframes glowPulse {
122    0%   { width: 120px; height: 200px; bottom: -20px; }
123    5%   { width: 40px; height: 60px; bottom: -10px; }
124    10%  { width: 120px; height: 200px; bottom: -20px; }
125    50%  { width: 120px; height: 200px; bottom: -20px; }
126    55%  { width: 40px; height: 60px; bottom: -10px; }
127    60%  { width: 120px; height: 200px; bottom: -20px; }
128    100% { width: 120px; height: 200px; bottom: -20px; }
129  }
130</style>
131</head>
132<body>
133<div class="scene">
134  <div class="lightsaber">
135    <div class="glow"></div>
136    <div class="blade"></div>
137    <div class="hilt"></div>
138  </div>
139</div>
140
141<script>
142  // Use JS to drive a more precise animation with canvas for better control
143  const scene = document.querySelector('.scene');
144  scene.innerHTML = '';
145  
146  const canvas = document.createElement('canvas');
147  canvas.width = 1024;
148  canvas.height = 768;
149  canvas.style.width = '100%';
150  canvas.style.height = '100%';
151  scene.appendChild(canvas);
152  
153  const ctx = canvas.getContext('2d');
154  
155  // Animation state
156  let startTime = null;
157  const totalDuration = 8000; // 8 seconds per cycle
158  
159  // Color phases: blue -> (shrink) -> green -> (shrink) -> red -> (shrink) -> blue...
160  // Based on frames: starts blue, swings, shrinks, green appears, swings, shrinks, red appears
161  
162  function getColor(t) {
163    // t is 0-1 within cycle
164    if (t < 0.45) return { r: 68, g: 136, b: 255, name: 'blue' };
165    if (t < 0.50) {
166      // transition blue->green
167      const p = (t - 0.45) / 0.05;
168      return { r: Math.round(68*(1-p)), g: Math.round(136 + (255-136)*p), b: Math.round(255*(1-p)), name: 'trans' };
169    }
170    if (t < 0.90) return { r: 0, g: 255, b: 68, name: 'green' };
171    if (t < 0.95) {
172      // transition green->red
173      const p = (t - 0.90) / 0.05;
174      return { r: Math.round(255*p), g: Math.round(255*(1-p)), b: 0, name: 'trans' };
175    }
176    return { r: 255, g: 34, b: 0, name: 'red' };
177  }
178  
179  function getAngle(t) {
180    // t is 0-1
181    // Swing pattern: starts near vertical (-5deg from vertical = pointing up slightly tilted)
182    // swings to ~130deg (pointing down-right), then back
183    // The pivot is at the hilt (bottom of blade)
184    // 0 = pointing straight up, positive = clockwise
185    
186    // Phase 1 (blue): 0 -> 0.12: swing from -5 to 130 deg (fast swing down)
187    // Phase 1: 0.12 -> 0.25: hold at ~135
188    // Phase 1: 0.25 -> 0.37: swing back to -10
189    // Phase 1: 0.37 -> 0.45: hold near vertical
190    // Shrink at 0.45-0.50
191    // Phase 2 (green): similar pattern
192    // Phase 2: 0.50 -> 0.62: swing from -5 to 130
193    // Phase 2: 0.62 -> 0.75: hold
194    // Phase 2: 0.75 -> 0.87: swing back
195    // Phase 2: 0.87 -> 0.90: hold
196    // Shrink at 0.90-0.95
197    // Phase 3 (red): 0.95 -> 1.0: appear vertical
198    
199    function easeInOut(x) {
200      return x < 0.5 ? 2*x*x : 1 - Math.pow(-2*x+2,2)/2;
201    }
202    
203    if (t < 0.12) {
204      return -5 + easeInOut(t/0.12) * 135;
205    } else if (t < 0.25) {
206      return 130;
207    } else if (t < 0.37) {
208      return 130 - easeInOut((t-0.25)/0.12) * 140;
209    } else if (t < 0.45) {
210      return -10;
211    } else if (t < 0.50) {
212      return -10; // shrinking
213    } else if (t < 0.62) {
214      return -5 + easeInOut((t-0.50)/0.12) * 135;
215    } else if (t < 0.75) {
216      return 130;
217    } else if (t < 0.87) {
218      return 130 - easeInOut((t-0.75)/0.12) * 140;
219    } else if (t < 0.90) {
220      return -10;
221    } else if (t < 0.95) {
222      return -10; // shrinking
223    } else {
224      // red appears, swing up
225      return -5 + easeInOut((t-0.95)/0.05) * (-5);
226    }
227  }
228  
229  function getBladeLength(t) {
230    // Shrink at transitions
231    const maxLen = 160;
232    const minLen = 8;
233    
234    function shrinkCurve(p) {
235      // p: 0=full, 0.5=min, 1=full
236      if (p < 0.5) return maxLen - (maxLen - minLen) * (p/0.5);
237      return minLen + (maxLen - minLen) * ((p-0.5)/0.5);
238    }
239    
240    if (t >= 0.44 && t < 0.52) {
241      return shrinkCurve((t - 0.44) / 0.08);
242    }
243    if (t >= 0.89 && t < 0.97) {
244      return shrinkCurve((t - 0.89) / 0.08);
245    }
246    return maxLen;
247  }
248  
249  function draw(timestamp) {
250    if (!startTime) startTime = timestamp;
251    const elapsed = (timestamp - startTime) % totalDuration;
252    const t = elapsed / totalDuration;
253    
254    ctx.clearRect(0, 0, 1024, 768);
255    ctx.fillStyle = '#000';
256    ctx.fillRect(0, 0, 1024, 768);
257    
258    const color = getColor(t);
259    const angleDeg = getAngle(t);
260    const bladeLen = getBladeLength(t);
261    
262    // Hilt position (center of scene, slightly right and up from center)
263    const hiltX = 512;
264    const hiltY = 390;
265    
266    const angleRad = (angleDeg - 90) * Math.PI / 180; // -90 so 0deg = pointing up
267    
268    // Blade tip
269    const tipX = hiltX + Math.cos(angleRad) * bladeLen;
270    const tipY = hiltY + Math.sin(angleRad) * bladeLen;
271    
272    // Draw ambient glow
273    const glowRadius = 80;
274    const midX = hiltX + Math.cos(angleRad) * bladeLen * 0.5;
275    const midY = hiltY + Math.sin(angleRad) * bladeLen * 0.5;
276    
277    const grad = ctx.createRadialGradient(midX, midY, 0, midX, midY, glowRadius);
278    grad.addColorStop(0, `rgba(${color.r},${color.g},${color.b},0.15)`);
279    grad.addColorStop(1, 'rgba(0,0,0,0)');
280    ctx.fillStyle = grad;
281    ctx.beginPath();
282    ctx.ellipse(midX, midY, glowRadius, glowRadius * 1.2, angleRad, 0, Math.PI * 2);
283    ctx.fill();
284    
285    // Draw blade glow (wide)
286    ctx.save();
287    ctx.lineCap = 'round';
288    
289    // Outer glow
290    ctx.beginPath();
291    ctx.moveTo(hiltX, hiltY);
292    ctx.lineTo(tipX, tipY);
293    ctx.strokeStyle = `rgba(${color.r},${color.g},${color.b},0.15)`;
294    ctx.lineWidth = 20;
295    ctx.stroke();
296    
297    // Mid glow
298    ctx.beginPath();
299    ctx.moveTo(hiltX, hiltY);
300    ctx.lineTo(tipX, tipY);
301    ctx.strokeStyle = `rgba(${color.r},${color.g},${color.b},0.4)`;
302    ctx.lineWidth = 8;
303    ctx.stroke();
304    
305    // Core blade
306    const bladeGrad = ctx.createLinearGradient(hiltX, hiltY, tipX, tipY);
307    bladeGrad.addColorStop(0, `rgba(${color.r},${color.g},${color.b},1)`);
308    bladeGrad.addColorStop(0.7, `rgba(${color.r},${color.g},${color.b},1)`);
309    bladeGrad.addColorStop(1, `rgba(${Math.min(255,color.r+100)},${Math.min(255,color.g+100)},${Math.min(255,color.b+100)},0.8)`);
310    
311    ctx.beginPath();
312    ctx.moveTo(hiltX, hiltY);
313    ctx.lineTo(tipX, tipY);
314    ctx.strokeStyle = bladeGrad;
315    ctx.lineWidth = 3;
316    ctx.stroke();
317    
318    // White core
319    ctx.beginPath();
320    ctx.moveTo(hiltX, hiltY);
321    ctx.lineTo(tipX, tipY);
322    ctx.strokeStyle = 'rgba(255,255,255,0.9)';
323    ctx.lineWidth = 1.5;
324    ctx.stroke();
325    
326    ctx.restore();
327    
328    // Draw hilt
329    const hiltLen = 20;
330    const hiltEndX = hiltX - Math.cos(angleRad) * hiltLen;
331    const hiltEndY = hiltY - Math.sin(angleRad) * hiltLen;
332    
333    ctx.save();
334    ctx.lineCap = 'round';
335    ctx.beginPath();
336    ctx.moveTo(hiltX, hiltY);
337    ctx.lineTo(hiltEndX, hiltEndY);
338    ctx.strokeStyle = '#cccccc';
339    ctx.lineWidth = 5;
340    ctx.stroke();
341    
342    ctx.beginPath();
343    ctx.moveTo(hiltX, hiltY);
344    ctx.lineTo(hiltEndX, hiltEndY);
345    ctx.strokeStyle = '#888888';
346    ctx.lineWidth = 3;
347    ctx.stroke();
348    ctx.restore();
349    
350    requestAnimationFrame(draw);
351  }
352  
353  requestAnimationFrame(draw);
354</script>
355</body>
356</html>