animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to SVG Multi-Drip

model output

GPT-5.4

SVG Multi-Drip

A 0.90T 0.24
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>Blob Wave Dots Animation</title>
7<style>
8  :root{
9    --bg:#2f2f2f;
10    --mag:#f000ff;
11    --red:#ff120d;
12  }
13  html,body{
14    margin:0;
15    height:100%;
16    background:var(--bg);
17    overflow:hidden;
18    font-family:sans-serif;
19  }
20  canvas{
21    display:block;
22    width:100vw;
23    height:100vh;
24  }
25</style>
26</head>
27<body>
28<canvas id="c"></canvas>
29<script>
30const canvas = document.getElementById('c');
31const ctx = canvas.getContext('2d');
32
33function resize(){
34  const dpr = Math.min(devicePixelRatio || 1, 2);
35  canvas.width = innerWidth * dpr;
36  canvas.height = innerHeight * dpr;
37  canvas.style.width = innerWidth + 'px';
38  canvas.style.height = innerHeight + 'px';
39  ctx.setTransform(dpr,0,0,dpr,0,0);
40}
41addEventListener('resize', resize);
42resize();
43
44const TAU = Math.PI * 2;
45const lerp = (a,b,t)=>a+(b-a)*t;
46const clamp = (v,a,b)=>Math.max(a,Math.min(b,v));
47const smooth = t => t*t*(3-2*t);
48const easeInOut = t => 0.5 - 0.5*Math.cos(Math.PI*t);
49
50function gradColor(t){
51  t = clamp(t,0,1);
52  const c1 = [240,0,255];
53  const c2 = [255,18,13];
54  const r = Math.round(lerp(c1[0],c2[0],t));
55  const g = Math.round(lerp(c1[1],c2[1],t));
56  const b = Math.round(lerp(c1[2],c2[2],t));
57  return `rgb(${r},${g},${b})`;
58}
59
60function pulse(x, c, w){
61  const d = Math.abs(x-c);
62  if(d >= w) return 0;
63  return 0.5 + 0.5*Math.cos(Math.PI * d / w);
64}
65
66function drawCircle(x,y,r,color){
67  ctx.beginPath();
68  ctx.arc(x,y,r,0,TAU);
69  ctx.fillStyle = color;
70  ctx.fill();
71}
72
73function metaballChain(points, baseR, ampFn, colorFn){
74  const step = 4;
75  for(let x = points[0].x - baseR; x <= points[points.length-1].x + baseR; x += step){
76    let top = Infinity, bot = -Infinity, hit = false;
77    for(let i=0;i<points.length;i++){
78      const p = points[i];
79      const dx = x - p.x;
80      const r = baseR + ampFn(i,p);
81      if(Math.abs(dx) <= r){
82        const h = Math.sqrt(r*r - dx*dx);
83        top = Math.min(top, p.y - h);
84        bot = Math.max(bot, p.y + h);
85        hit = true;
86      }
87    }
88    if(hit){
89      ctx.strokeStyle = colorFn((x - points[0].x)/(points[points.length-1].x - points[0].x));
90      ctx.lineWidth = step + 1;
91      ctx.beginPath();
92      ctx.moveTo(x, top);
93      ctx.lineTo(x, bot);
94      ctx.stroke();
95    }
96  }
97}
98
99function draw(){
100  const w = innerWidth, h = innerHeight;
101  ctx.clearRect(0,0,w,h);
102
103  const left = w * 0.145;
104  const right = w * 0.812;
105  const topY = h * 0.245;
106  const botY = h * 0.415;
107
108  const n = 16;
109  const xs = [];
110  for(let i=0;i<n;i++) xs.push(lerp(left,right,i/(n-1)));
111
112  const t = (performance.now() * 0.001);
113  const cycle = 6.4;
114  const p = (t % cycle) / cycle; // 0..1
115
116  // traveling disturbance left -> right
117  const head = lerp(-0.06, 1.08, p);
118  const width = 0.18;
119
120  // top chain points
121  const topPts = xs.map((x,i)=>({x,y:topY}));
122  metaballChain(
123    topPts,
124    18,
125    (i,pnt)=>{
126      const u = i/(n-1);
127      const wave = 2.2*Math.sin(i*1.9 + t*10.5);
128      const bump = 7 * pulse(u, head, width);
129      return wave + bump;
130    },
131    u=>gradColor(u)
132  );
133
134  // lower dotted guide row
135  for(let i=0;i<n;i++){
136    const u = i/(n-1);
137    const x = xs[i];
138    const local = pulse(u, head, width*1.15);
139    const y = botY + 10*Math.sin((u*8.5 - p*7.5)*Math.PI) * local;
140    const r = lerp(2.6,4.2, local*0.9);
141    drawCircle(x,y,r,gradColor(u));
142  }
143
144  // bouncing detached blobs between rows
145  for(let i=0;i<n;i++){
146    const u = i/(n-1);
147    const influence = pulse(u, head, width);
148    if(influence < 0.02) continue;
149
150    const phase = clamp((head - u)/width * 0.5 + 0.5, 0, 1);
151    const arc = Math.sin(phase * Math.PI);
152    const x = xs[i];
153    const y = lerp(botY - 28, topY + 8, arc) - 18*influence*Math.sin((u*11 - p*9)*Math.PI);
154    const r = 7 + 12*influence*(0.45 + 0.55*arc);
155    drawCircle(x,y,r,gradColor(u));
156  }
157
158  // downward drips from top bar
159  for(let i=0;i<n;i++){
160    const u = i/(n-1);
161    const lagHead = head - 0.08;
162    const inf = pulse(u, lagHead, width*0.72);
163    if(inf < 0.12) continue;
164
165    const drop = 10 + 28*inf;
166    const x = xs[i];
167    const r = 8 + 6*inf;
168    ctx.strokeStyle = gradColor(u);
169    ctx.lineCap = 'round';
170    ctx.lineWidth = r*1.55;
171    ctx.beginPath();
172    ctx.moveTo(x, topY + 2);
173    ctx.lineTo(x, topY + drop);
174    ctx.stroke();
175    drawCircle(x, topY + drop + r*0.15, r, gradColor(u));
176  }
177
178  requestAnimationFrame(draw);
179}
180draw();
181</script>
182</body>
183</html>