← back to Animated Dot Loaderscompare with ground truth →
model output
Claude Sonnet 4.6
Animated Dot Loaders
A 0.87T 0.22
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>Loading Animation</title>
7<style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 width: 100vw;
16 height: 100vh;
17 background: radial-gradient(ellipse at center, #1a0a2e 0%, #0d0818 40%, #080610 100%);
18 display: flex;
19 align-items: center;
20 justify-content: center;
21 overflow: hidden;
22 }
23
24 .glow-bg {
25 position: absolute;
26 width: 300px;
27 height: 300px;
28 border-radius: 50%;
29 background: radial-gradient(ellipse at center, rgba(80, 20, 120, 0.3) 0%, transparent 70%);
30 border: 1px solid rgba(100, 40, 150, 0.15);
31 top: 50%;
32 left: 50%;
33 transform: translate(-50%, -50%);
34 }
35
36 .dots-container {
37 display: flex;
38 align-items: center;
39 gap: 10px;
40 position: relative;
41 z-index: 1;
42 }
43
44 .dot {
45 width: 14px;
46 height: 14px;
47 border-radius: 50%;
48 background: #7a7a9a;
49 animation: dotPulse 1.5s ease-in-out infinite;
50 }
51
52 .dot:nth-child(1) { animation-delay: 0s; }
53 .dot:nth-child(2) { animation-delay: 0.15s; }
54 .dot:nth-child(3) { animation-delay: 0.3s; }
55 .dot:nth-child(4) { animation-delay: 0.45s; }
56 .dot:nth-child(5) { animation-delay: 0.6s; }
57
58 @keyframes dotPulse {
59 0% {
60 background: #5a5a7a;
61 transform: scale(0.85);
62 opacity: 0.5;
63 }
64 50% {
65 background: #ffffff;
66 transform: scale(1.1);
67 opacity: 1;
68 }
69 100% {
70 background: #5a5a7a;
71 transform: scale(0.85);
72 opacity: 0.5;
73 }
74 }
75
76 /* The animation shows dots appearing/disappearing - the bright dot travels from right to left */
77 /* We'll use a wave that moves the bright spot from right to left */
78</style>
79</head>
80<body>
81 <div class="glow-bg"></div>
82 <div class="dots-container">
83 <div class="dot"></div>
84 <div class="dot"></div>
85 <div class="dot"></div>
86 <div class="dot"></div>
87 <div class="dot"></div>
88 </div>
89
90 <script>
91 // Looking at the frames: dots collapse from left (disappear) while bright dot moves right to left
92 // Frame 1: 5 dots visible, rightmost bright
93 // Frame 2: 4 dots, rightmost bright
94 // Frame 3: 3 dots, rightmost bright
95 // Frame 4: 1 dot (center), very bright
96 // Frame 5: 1 dot (center), bright
97 // Frame 6: 3 dots appear, rightmost bright
98 // Frame 7: 4 dots, rightmost bright
99 // Frame 8: 3 dots, rightmost bright
100
101 // This looks like dots are collapsing to center then expanding
102 // Let's implement with JS for precise control
103
104 const container = document.querySelector('.dots-container');
105 const dots = document.querySelectorAll('.dot');
106
107 // Override CSS animation with JS-controlled animation
108 dots.forEach(d => {
109 d.style.animation = 'none';
110 d.style.background = '#5a5a7a';
111 d.style.opacity = '0.5';
112 d.style.transform = 'scale(0.85)';
113 });
114
115 // The bright "active" dot travels: from right side, collapses to center, then expands back
116 // Total cycle: ~2s
117 // Phase 1 (0-0.8s): bright dot moves from index 4 to index 0 (right to left), dots disappear
118 // Phase 2 (0.8-1.0s): single bright dot at center
119 // Phase 3 (1.0-1.8s): dots reappear, bright dot at rightmost visible
120 // Phase 4 (1.8-2.0s): pause
121
122 function lerp(a, b, t) { return a + (b - a) * t; }
123
124 const CYCLE = 2000;
125 const NUM_DOTS = 5;
126 const CENTER = 2; // index of center dot
127
128 function easeInOut(t) {
129 return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
130 }
131
132 function update() {
133 const now = performance.now();
134 const t = (now % CYCLE) / CYCLE; // 0 to 1
135
136 // Phase 1: collapse (0 to 0.45) - dots disappear from left, bright moves left
137 // Phase 2: single dot (0.45 to 0.55)
138 // Phase 3: expand (0.55 to 1.0) - dots appear from center outward, bright at right
139
140 dots.forEach((dot, i) => {
141 dot.style.display = 'block';
142 });
143
144 if (t < 0.45) {
145 // Collapsing: bright dot moves from right (index 4) toward center (index 2)
146 const p = t / 0.45; // 0 to 1
147 const ep = easeInOut(p);
148
149 // Number of visible dots decreases from 5 to 1
150 const visibleCount = Math.round(lerp(5, 1, ep));
151 const startIdx = CENTER - Math.floor((visibleCount - 1) / 2);
152
153 dots.forEach((dot, i) => {
154 const visible = i >= startIdx && i < startIdx + visibleCount;
155 dot.style.opacity = visible ? '0.5' : '0';
156 dot.style.transform = visible ? 'scale(0.85)' : 'scale(0)';
157 dot.style.background = '#5a5a7a';
158 });
159
160 // Bright dot is at the rightmost visible
161 const brightIdx = startIdx + visibleCount - 1;
162 if (brightIdx >= 0 && brightIdx < NUM_DOTS) {
163 const bright = dots[brightIdx];
164 bright.style.background = '#ffffff';
165 bright.style.opacity = '1';
166 bright.style.transform = 'scale(1.1)';
167 }
168
169 } else if (t < 0.55) {
170 // Single bright dot at center
171 dots.forEach((dot, i) => {
172 if (i === CENTER) {
173 dot.style.opacity = '1';
174 dot.style.transform = 'scale(1.1)';
175 dot.style.background = '#ffffff';
176 } else {
177 dot.style.opacity = '0';
178 dot.style.transform = 'scale(0)';
179 dot.style.background = '#5a5a7a';
180 }
181 });
182
183 } else {
184 // Expanding: dots appear from center outward, bright at rightmost
185 const p = (t - 0.55) / 0.45; // 0 to 1
186 const ep = easeInOut(p);
187
188 const visibleCount = Math.round(lerp(1, 5, ep));
189 const startIdx = CENTER - Math.floor((visibleCount - 1) / 2);
190
191 dots.forEach((dot, i) => {
192 const visible = i >= startIdx && i < startIdx + visibleCount;
193 dot.style.opacity = visible ? '0.5' : '0';
194 dot.style.transform = visible ? 'scale(0.85)' : 'scale(0)';
195 dot.style.background = '#5a5a7a';
196 });
197
198 // Bright dot at rightmost visible
199 const brightIdx = startIdx + visibleCount - 1;
200 if (brightIdx >= 0 && brightIdx < NUM_DOTS) {
201 const bright = dots[brightIdx];
202 bright.style.background = '#ffffff';
203 bright.style.opacity = '1';
204 bright.style.transform = 'scale(1.1)';
205 }
206 }
207
208 requestAnimationFrame(update);
209 }
210
211 requestAnimationFrame(update);
212 </script>
213</body>
214</html>