animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Bubble Float

model output

GPT-5.4

Bubble Float

A 0.89T 0.47
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>Floating Bubbles</title>
7<style>
8  :root{
9    --bg:#1f2329;
10    --bubble: rgba(110, 180, 190, 0.58);
11  }
12
13  html,body{
14    margin:0;
15    width:100%;
16    height:100%;
17    overflow:hidden;
18    background:var(--bg);
19  }
20
21  body{
22    position:relative;
23    font-family:sans-serif;
24  }
25
26  #scene{
27    position:fixed;
28    inset:0;
29    overflow:hidden;
30    background:var(--bg);
31  }
32
33  .bubble{
34    position:absolute;
35    border-radius:50%;
36    background:var(--bubble);
37    transform:translate3d(0,0,0);
38    will-change:transform, opacity;
39    pointer-events:none;
40  }
41</style>
42</head>
43<body>
44<div id="scene"></div>
45
46<script>
47(() => {
48  const scene = document.getElementById('scene');
49  const W = () => innerWidth;
50  const H = () => innerHeight;
51
52  const bubbles = [];
53  const TOTAL = 26;
54
55  // Slow start like the reference: mostly empty, then bubbles rise in.
56  function spawn(i){
57    const el = document.createElement('div');
58    el.className = 'bubble';
59
60    const sideBias = Math.random();
61    let x;
62    if (sideBias < 0.38) x = Math.random() * W() * 0.28;                 // left cluster
63    else if (sideBias > 0.62) x = W() * (0.72 + Math.random() * 0.28);   // right cluster
64    else x = W() * (0.25 + Math.random() * 0.5);                         // occasional center
65
66    const r = 6 + Math.random() * 34;
67    const y = H() + r + Math.random() * H() * 0.55;
68
69    const speed = 12 + (42 - r) * 0.22 + Math.random() * 10; // px/sec, larger bubbles slightly slower
70    const drift = (Math.random() - 0.5) * 18;
71    const phase = Math.random() * Math.PI * 2;
72    const amp = 6 + Math.random() * 18;
73    const opacity = 0.42 + Math.random() * 0.22;
74
75    el.style.width = el.style.height = `${r * 2}px`;
76    el.style.opacity = opacity.toFixed(3);
77    scene.appendChild(el);
78
79    bubbles.push({
80      el, x, y, r, speed, drift, phase, amp,
81      born: performance.now() + i * 420 + (i < 2 ? 2600 : 0) // long empty intro, then gradual appearance
82    });
83  }
84
85  for(let i=0;i<TOTAL;i++) spawn(i);
86
87  function resetBubble(b, delay = 0){
88    const sideBias = Math.random();
89    if (sideBias < 0.42) b.x = Math.random() * W() * 0.3;
90    else if (sideBias > 0.58) b.x = W() * (0.7 + Math.random() * 0.3);
91    else b.x = W() * (0.2 + Math.random() * 0.6);
92
93    b.r = 6 + Math.random() * 34;
94    b.y = H() + b.r + Math.random() * 120;
95    b.speed = 12 + (42 - b.r) * 0.22 + Math.random() * 10;
96    b.drift = (Math.random() - 0.5) * 18;
97    b.phase = Math.random() * Math.PI * 2;
98    b.amp = 6 + Math.random() * 18;
99    b.born = performance.now() + delay;
100
101    b.el.style.width = b.el.style.height = `${b.r * 2}px`;
102    b.el.style.opacity = (0.42 + Math.random() * 0.22).toFixed(3);
103  }
104
105  let last = performance.now();
106
107  function tick(now){
108    const dt = Math.min(0.033, (now - last) / 1000);
109    last = now;
110
111    for(const b of bubbles){
112      if(now < b.born){
113        b.el.style.transform = `translate3d(-9999px,-9999px,0)`;
114        continue;
115      }
116
117      b.y -= b.speed * dt;
118
119      const t = now * 0.001;
120      const x = b.x + Math.sin(t * 0.8 + b.phase) * b.amp + b.drift * (t * 0.12);
121      const y = b.y;
122
123      b.el.style.transform = `translate3d(${x - b.r}px, ${y - b.r}px, 0)`;
124
125      if(y < -b.r * 2 - 20){
126        resetBubble(b, 300 + Math.random() * 1800);
127      }
128    }
129
130    requestAnimationFrame(tick);
131  }
132
133  addEventListener('resize', () => {
134    for(const b of bubbles){
135      b.x = Math.max(-40, Math.min(W() + 40, b.x));
136      b.y = Math.max(-100, Math.min(H() + 200, b.y));
137    }
138  });
139
140  requestAnimationFrame(tick);
141})();
142</script>
143</body>
144</html>