← back to Bubble Floatcompare with ground truth →
model output
Gemini 3 Flash Preview
Bubble Float
A 0.89T 0.19
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>Floating Circles Animation</title>
7 <style>
8 :root {
9 --bg-color: #1a1e23;
10 --circle-color: rgba(95, 137, 142, 0.6);
11 }
12
13 body, html {
14 margin: 0;
15 padding: 0;
16 width: 100%;
17 height: 100%;
18 background-color: var(--bg-color);
19 overflow: hidden;
20 display: flex;
21 justify-content: center;
22 align-items: center;
23 }
24
25 .container {
26 position: relative;
27 width: 100%;
28 height: 100%;
29 }
30
31 .circle {
32 position: absolute;
33 background-color: var(--circle-color);
34 border-radius: 50%;
35 pointer-events: none;
36 opacity: 0;
37 transform: translate(-50%, -50%);
38 will-change: transform, opacity;
39 }
40
41 @keyframes floatUp {
42 0% {
43 transform: translate(-50%, 100vh) scale(0.8);
44 opacity: 0;
45 }
46 10% {
47 opacity: 0.6;
48 }
49 100% {
50 transform: translate(calc(-50% + var(--drift)), -20vh) scale(1.1);
51 opacity: 0;
52 }
53 }
54 </style>
55</head>
56<body>
57
58 <div class="container" id="container"></div>
59
60 <script>
61 const container = document.getElementById('container');
62
63 /**
64 * Creates a circle element with randomized properties
65 * @param {number} x - Horizontal position percentage (0-100)
66 * @param {number} size - Diameter in pixels
67 * @param {number} duration - Animation duration in seconds
68 * @param {number} drift - Horizontal drift in pixels
69 */
70 function spawnCircle(x, size, duration, drift) {
71 const circle = document.createElement('div');
72 circle.className = 'circle';
73
74 circle.style.width = `${size}px`;
75 circle.style.height = `${size}px`;
76 circle.style.left = `${x}%`;
77 circle.style.bottom = `-100px`; // Start off-screen
78 circle.style.setProperty('--drift', `${drift}px`);
79
80 circle.style.animation = `floatUp ${duration}s cubic-bezier(0.4, 0, 0.6, 1) forwards`;
81
82 container.appendChild(circle);
83
84 // Cleanup
85 setTimeout(() => {
86 circle.remove();
87 }, duration * 1000);
88 }
89
90 // Timing sequence matching the video
91 // Video starts blank, first circle at 3.5s
92
93 setTimeout(() => {
94 // First circle (bottom center)
95 spawnCircle(50, 60, 5, -20);
96 }, 3500);
97
98 setTimeout(() => {
99 // Small circle far right
100 spawnCircle(90, 15, 6, -10);
101 }, 4200);
102
103 setTimeout(() => {
104 // Large ones right side
105 spawnCircle(95, 110, 7, -50);
106 spawnCircle(85, 40, 6.5, -30);
107 }, 5000);
108
109 setTimeout(() => {
110 // Left side group
111 spawnCircle(10, 80, 5.5, 40);
112 spawnCircle(20, 100, 6, 20);
113 spawnCircle(5, 50, 6.5, 30);
114 }, 5500);
115
116 // Continuous loop to simulate the ending of the video's trend
117 let spawnInterval;
118 setTimeout(() => {
119 spawnInterval = setInterval(() => {
120 const x = Math.random() * 100;
121 const size = Math.random() * 80 + 20;
122 const duration = Math.random() * 4 + 4;
123 const drift = (Math.random() - 0.5) * 150;
124 spawnCircle(x, size, duration, drift);
125 }, 400);
126 }, 6000);
127
128 // Optional: Stop spawning after 15 seconds for demonstration purposes
129 setTimeout(() => {
130 clearInterval(spawnInterval);
131 }, 15000);
132
133 </script>
134</body>
135</html>