animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Merry Christmas Tree!

model output

GPT-5.4

Merry Christmas Tree!

A 0.79T 0.30
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" />
6<title>Christmas Tree Line Animation</title>
7<style>
8  :root{
9    --bg:#171726;
10    --gold:#d8a06f;
11    --gold2:#e0b07f;
12    --white:#f5f7ff;
13    --blue:#0b6dff;
14    --text:#f2f0f4;
15  }
16  html,body{
17    margin:0;
18    height:100%;
19    background:var(--bg);
20    overflow:hidden;
21    font-family: Georgia, serif;
22  }
23  svg{
24    width:100vw;
25    height:100vh;
26    display:block;
27    background:var(--bg);
28  }
29
30  .gold-line{
31    fill:none;
32    stroke:url(#goldGrad);
33    stroke-width:7;
34    stroke-linecap:round;
35    stroke-linejoin:round;
36    filter:url(#softGlow);
37  }
38
39  .thin-tail{
40    fill:none;
41    stroke:url(#goldGrad);
42    stroke-width:2.2;
43    stroke-linecap:round;
44    opacity:.95;
45  }
46
47  .star-top{
48    fill:var(--gold);
49    opacity:0;
50    transform-box:fill-box;
51    transform-origin:center;
52    animation:topStarIn .45s ease-out forwards;
53    animation-delay:4.55s;
54  }
55
56  .star-outline{
57    fill:none;
58    stroke:#d9d8e8;
59    stroke-width:3;
60    opacity:0;
61    transform-box:fill-box;
62    transform-origin:center;
63    animation:starOutline .55s ease-out forwards;
64    animation-delay:7.2s;
65  }
66
67  .caption{
68    fill:var(--text);
69    font-size:34px;
70    letter-spacing:.5px;
71    opacity:0;
72    filter:url(#textGlow);
73    animation:captionIn .8s ease-out forwards;
74    animation-delay:6.2s;
75  }
76
77  .spark{
78    opacity:0;
79    animation:twinkle 1.2s ease-in-out infinite;
80  }
81
82  .trail-star{
83    filter:url(#blueGlow);
84  }
85
86  .draw{
87    stroke-dasharray:var(--len);
88    stroke-dashoffset:var(--len);
89    animation:draw var(--dur) linear forwards;
90    animation-delay:var(--delay);
91  }
92
93  #orb{
94    filter:url(#blueGlow);
95  }
96
97  @keyframes draw{
98    to{ stroke-dashoffset:0; }
99  }
100  @keyframes twinkle{
101    0%,100%{ opacity:.15; transform:scale(.7); }
102    50%{ opacity:.95; transform:scale(1.15); }
103  }
104  @keyframes captionIn{
105    from{ opacity:0; transform:translateY(10px); }
106    to{ opacity:1; transform:translateY(0); }
107  }
108  @keyframes topStarIn{
109    0%{ opacity:0; transform:scale(.2) rotate(-20deg); }
110    70%{ opacity:1; transform:scale(1.08) rotate(6deg); }
111    100%{ opacity:1; transform:scale(1) rotate(0); }
112  }
113  @keyframes starOutline{
114    0%{ opacity:0; transform:scale(.7); }
115    100%{ opacity:1; transform:scale(1); }
116  }
117</style>
118</head>
119<body>
120<svg viewBox="0 0 1024 768" aria-label="Animated Christmas tree drawing">
121  <defs>
122    <linearGradient id="goldGrad" x1="0" y1="0" x2="1" y2="0">
123      <stop offset="0%" stop-color="#c98f61"/>
124      <stop offset="50%" stop-color="#e0b07f"/>
125      <stop offset="100%" stop-color="#c98f61"/>
126    </linearGradient>
127
128    <filter id="softGlow" x="-50%" y="-50%" width="200%" height="200%">
129      <feGaussianBlur stdDeviation="0.35" result="b"/>
130      <feMerge>
131        <feMergeNode in="b"/>
132        <feMergeNode in="SourceGraphic"/>
133      </feMerge>
134    </filter>
135
136    <filter id="blueGlow" x="-300%" y="-300%" width="700%" height="700%">
137      <feGaussianBlur stdDeviation="12" result="blur1"/>
138      <feColorMatrix in="blur1" type="matrix"
139        values="0 0 0 0 0.02
140                0 0 0 0 0.42
141                0 0 0 0 1
142                0 0 0 0.75 0" result="blue"/>
143      <feGaussianBlur in="SourceGraphic" stdDeviation="1.2" result="core"/>
144      <feMerge>
145        <feMergeNode in="blue"/>
146        <feMergeNode in="core"/>
147      </feMerge>
148    </filter>
149
150    <filter id="textGlow" x="-20%" y="-20%" width="140%" height="140%">
151      <feGaussianBlur stdDeviation=".4"/>
152    </filter>
153
154    <path id="p1" d="M334 552
155      C338 540, 350 528, 372 516
156      C410 495, 446 478, 434 458
157      C422 438, 372 444, 367 427
158      C362 410, 410 392, 448 370
159      C486 348, 486 332, 458 324
160      C430 316, 406 318, 414 306
161      C438 274, 462 244, 486 196
162      C500 168, 508 146, 521 132" />
163
164    <path id="p2" d="M521 132
165      C534 146, 542 168, 556 196
166      C580 244, 604 274, 628 306
167      C636 318, 612 316, 584 324
168      C556 332, 556 348, 594 370
169      C632 392, 680 410, 675 427
170      C670 444, 620 438, 608 458
171      C596 478, 632 495, 670 516
172      C692 528, 704 540, 708 552" />
173
174    <path id="p3" d="M292 594
175      C360 566, 446 592, 520 604
176      C594 616, 676 620, 732 596
177      C744 591, 752 584, 756 576" />
178
179    <path id="trunk" d="M482 618
180      C480 640, 486 652, 520 652
181      C554 652, 560 640, 558 618" />
182
183    <path id="tailL" d="M334 552 C326 544, 326 536, 340 526" />
184    <path id="tailR" d="M708 552 C718 544, 718 536, 704 526" />
185  </defs>
186
187  <!-- static twinkles -->
188  <g id="ambient"></g>
189
190  <!-- drawn tree -->
191  <use href="#p1" class="gold-line draw" style="--len:560;--dur:1.9s;--delay:.45s"/>
192  <use href="#p2" class="gold-line draw" style="--len:560;--dur:1.9s;--delay:2.35s"/>
193  <use href="#p3" class="gold-line draw" style="--len:500;--dur:1.45s;--delay:5.05s"/>
194  <use href="#trunk" class="gold-line draw" style="--len:120;--dur:.45s;--delay:4.95s"/>
195  <use href="#tailL" class="thin-tail draw" style="--len:40;--dur:.25s;--delay:1.95s"/>
196  <use href="#tailR" class="thin-tail draw" style="--len:40;--dur:.25s;--delay:4.15s"/>
197
198  <!-- top star -->
199  <polygon class="star-top" points="521,78 530,101 555,103 536,118 542,143 521,130 500,143 506,118 487,103 512,101"/>
200  <polygon class="star-outline" points="521,74 531,99 558,101 538,118 544,147 521,133 498,147 504,118 484,101 511,99"/>
201
202  <!-- moving orb and spark trail -->
203  <g id="orbWrap">
204    <g id="orb">
205      <circle r="4.2" fill="#fff"/>
206      <circle r="2.2" fill="#fff"/>
207    </g>
208    <g id="trailStars"></g>
209  </g>
210
211  <!-- caption -->
212  <text class="caption" x="512" y="708" text-anchor="middle"
213    style="font-family:'Papyrus','Bradley Hand','Comic Sans MS',cursive;">
214    This is available to buy at my shop
215  </text>
216</svg>
217
218<script>
219(() => {
220  const svg = document.querySelector('svg');
221  const orb = document.getElementById('orbWrap');
222  const trailStars = document.getElementById('trailStars');
223  const ambient = document.getElementById('ambient');
224
225  const NS = "http://www.w3.org/2000/svg";
226
227  const segments = [
228    { id:'p1', start:450, dur:1900 },
229    { id:'p2', start:2350, dur:1900 },
230    { id:'p3', start:5050, dur:1450 }
231  ];
232
233  const total = 8200;
234
235  const colors = ['#ffffff','#dfe8ff','#b8f7ff','#7fdc8a','#b10f3a','#d94b5f','#7a7f92'];
236  const rand = (a,b)=>a+Math.random()*(b-a);
237
238  function starPoints(r1, r2, n=5){
239    let pts = [];
240    for(let i=0;i<n*2;i++){
241      const a = -Math.PI/2 + i*Math.PI/n;
242      const r = i%2===0 ? r1 : r2;
243      pts.push((Math.cos(a)*r).toFixed(2)+','+(Math.sin(a)*r).toFixed(2));
244    }
245    return pts.join(' ');
246  }
247
248  function addAmbient(){
249    const pts = [
250      [332,672],[352,648],[398,596],[438,506],[498,438],[510,370],[548,420],[590,468],[648,548],[682,612],
251      [470,610],[610,610],[430,690],[690,690],[360,560],[660,560],[492,300],[560,340],[450,350],[520,520]
252    ];
253    pts.forEach((p,i)=>{
254      const g = document.createElementNS(NS,'g');
255      g.setAttribute('class','spark');
256      g.style.animationDelay = `${rand(0,1.2).toFixed(2)}s`;
257      g.setAttribute('transform',`translate(${p[0]} ${p[1]}) scale(${rand(.5,1.1).toFixed(2)})`);
258      const shape = Math.random()>.45 ? 'polygon' : 'path';
259      if(shape==='polygon'){
260        const poly = document.createElementNS(NS,'polygon');
261        poly.setAttribute('points', starPoints(rand(2.2,4.8), rand(.9,2.1)));
262        poly.setAttribute('fill', colors[(Math.random()*colors.length)|0]);
263        poly.setAttribute('opacity', rand(.35,.9).toFixed(2));
264        g.appendChild(poly);
265      }else{
266        const path = document.createElementNS(NS,'path');
267        path.setAttribute('d','M-3 0H3M0 -3V3');
268        path.setAttribute('stroke', colors[(Math.random()*2)|0]);
269        path.setAttribute('stroke-width','1.2');
270        path.setAttribute('stroke-linecap','round');
271        path.setAttribute('opacity', rand(.35,.9).toFixed(2));
272        g.appendChild(path);
273      }
274      ambient.appendChild(g);
275    });
276  }
277
278  addAmbient();
279
280  const pathEls = segments.map(s => document.getElementById(s.id));
281  const lengths = pathEls.map(p => p.getTotalLength());
282
283  let trail = [];
284  let startTime = null;
285
286  function pointAt(globalMs){
287    for(let i=0;i<segments.length;i++){
288      const s = segments[i];
289      if(globalMs >= s.start && globalMs <= s.start + s.dur){
290        const t = (globalMs - s.start) / s.dur;
291        const eased = t;
292        const len = lengths[i] * eased;
293        return pathEls[i].getPointAtLength(len);
294      }
295    }
296    if(globalMs < segments[0].start){
297      return pathEls[0].getPointAtLength(0);
298    }
299    if(globalMs > segments[segments.length-1].start + segments[segments.length-1].dur){
300      return pathEls[pathEls.length-1].getPointAtLength(lengths[lengths.length-1]);
301    }
302    return null;
303  }
304
305  function spawnTrail(x,y){
306    if(Math.random() > 0.55) return;
307    const g = document.createElementNS(NS,'g');
308    g.setAttribute('class','trail-star');
309    g.setAttribute('transform',`translate(${x} ${y}) scale(${rand(.45,1.15).toFixed(2)}) rotate(${rand(0,180).toFixed(1)})`);
310    g.style.opacity = rand(.45,.95).toFixed(2);
311
312    if(Math.random() > .35){
313      const poly = document.createElementNS(NS,'polygon');
314      poly.setAttribute('points', starPoints(rand(5,10), rand(2.2,4.2)));
315      poly.setAttribute('fill', colors[(Math.random()*colors.length)|0]);
316      g.appendChild(poly);
317    }else{
318      const path = document.createElementNS(NS,'path');
319      path.setAttribute('d','M-5 0H5M0 -5V5');
320      path.setAttribute('stroke', colors[(Math.random()*2)|0]);
321      path.setAttribute('stroke-width','1.6');
322      path.setAttribute('stroke-linecap','round');
323      g.appendChild(path);
324    }
325
326    trailStars.appendChild(g);
327    trail.push({el:g, born:performance.now()});
328  }
329
330  function animate(now){
331    if(!startTime) startTime = now;
332    const t = now - startTime;
333
334    const p = pointAt(t);
335    if(p){
336      orb.setAttribute('transform', `translate(${p.x} ${p.y})`);
337      spawnTrail(p.x + rand(-10,10), p.y + rand(-10,10));
338    } else {
339      orb.style.opacity = 0;
340    }
341
342    trail = trail.filter(item=>{
343      const age = now - item.born;
344      const life = 1200;
345      if(age > life){
346        item.el.remove();
347        return false;
348      }
349      const k = age / life;
350      item.el.style.opacity = (1-k)*0.9;
351      item.el.setAttribute('transform',
352        item.el.getAttribute('transform').replace(/scale\([^)]+\)/, `scale(${(1 + k*0.35).toFixed(2)})`)
353      );
354      return true;
355    });
356
357    if(t < total) requestAnimationFrame(animate);
358    else orb.style.opacity = 0;
359  }
360
361  requestAnimationFrame(animate);
362})();
363</script>
364</body>
365</html>