← back to Wavy Loading Animationscompare with ground truth →
model output
GPT-5.4
Wavy Loading Animations
A 0.64T 0.26
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>Brush Spiral Loader</title>
7<style>
8 :root{
9 --bg:#f6f6f6;
10 --dark:#1f1f1f;
11 --mid:#8f8f8f;
12 --light:#d8d8d8;
13 --size:42px;
14 --stroke:5px;
15 --dur:1.35s;
16 }
17
18 html,body{
19 margin:0;
20 width:100%;
21 height:100%;
22 overflow:hidden;
23 background:var(--bg);
24 font-family:sans-serif;
25 }
26
27 .stage{
28 position:relative;
29 width:100vw;
30 height:100vh;
31 }
32
33 .loader{
34 position:absolute;
35 left:50%;
36 top:12%;
37 width:var(--size);
38 height:var(--size);
39 transform:translate(-50%,-50%);
40 }
41
42 /* initial blot that morphs into the spiral */
43 .seed{
44 position:absolute;
45 inset:0;
46 animation:seedOut var(--dur) linear infinite;
47 }
48
49 .seed::before{
50 content:"";
51 position:absolute;
52 left:50%;
53 top:50%;
54 width:26px;
55 height:18px;
56 background:var(--dark);
57 border-radius:55% 35% 45% 60% / 60% 45% 55% 40%;
58 transform:translate(-50%,-50%) rotate(-88deg);
59 filter:blur(.15px);
60 }
61
62 /* spiral ring */
63 .ring{
64 position:absolute;
65 inset:0;
66 border-radius:50%;
67 box-sizing:border-box;
68 opacity:0;
69 animation:ringPulse var(--dur) linear infinite;
70 }
71
72 .ring.r1{
73 border:var(--stroke) solid transparent;
74 border-top-color:var(--dark);
75 border-left-color:var(--dark);
76 transform:rotate(-38deg);
77 }
78
79 .ring.r2{
80 inset:7px;
81 border:4px solid transparent;
82 border-top-color:var(--mid);
83 border-left-color:var(--mid);
84 transform:rotate(18deg);
85 animation-name:ringPulse2;
86 }
87
88 .ring.r3{
89 inset:15px;
90 border:3px solid transparent;
91 border-top-color:var(--light);
92 border-left-color:var(--light);
93 transform:rotate(58deg);
94 animation-name:ringPulse3;
95 }
96
97 /* rough brush head riding the outer arc */
98 .brush{
99 position:absolute;
100 left:50%;
101 top:50%;
102 width:12px;
103 height:10px;
104 margin:-5px 0 0 -6px;
105 opacity:0;
106 animation:brushOrbit var(--dur) linear infinite;
107 transform-origin:0 0;
108 }
109
110 .brush::before{
111 content:"";
112 position:absolute;
113 width:100%;
114 height:100%;
115 background:var(--dark);
116 border-radius:60% 35% 55% 45% / 55% 45% 60% 40%;
117 filter:blur(.2px);
118 }
119
120 .brush::after{
121 content:"";
122 position:absolute;
123 right:-4px;
124 top:3px;
125 width:7px;
126 height:3px;
127 background:var(--dark);
128 border-radius:3px;
129 transform:rotate(18deg);
130 }
131
132 @keyframes seedOut{
133 0%,12%{
134 opacity:1;
135 transform:scale(.72) rotate(0deg);
136 }
137 20%{
138 opacity:1;
139 transform:scale(.9) rotate(18deg);
140 }
141 28%,100%{
142 opacity:0;
143 transform:scale(1) rotate(40deg);
144 }
145 }
146
147 @keyframes ringPulse{
148 0%,14%{
149 opacity:0;
150 transform:scale(.55) rotate(-70deg);
151 }
152 24%{
153 opacity:1;
154 transform:scale(.92) rotate(-42deg);
155 }
156 38%{
157 opacity:.45;
158 transform:scale(1) rotate(-10deg);
159 }
160 52%{
161 opacity:.18;
162 transform:scale(1) rotate(28deg);
163 }
164 66%{
165 opacity:1;
166 transform:scale(1) rotate(78deg);
167 }
168 80%{
169 opacity:.45;
170 transform:scale(1) rotate(118deg);
171 }
172 100%{
173 opacity:.18;
174 transform:scale(1) rotate(150deg);
175 }
176 }
177
178 @keyframes ringPulse2{
179 0%,18%{
180 opacity:0;
181 transform:scale(.5) rotate(-20deg);
182 }
183 28%{
184 opacity:.9;
185 transform:scale(.95) rotate(18deg);
186 }
187 42%{
188 opacity:.28;
189 transform:scale(1) rotate(58deg);
190 }
191 66%{
192 opacity:.75;
193 transform:scale(1) rotate(138deg);
194 }
195 84%{
196 opacity:.28;
197 transform:scale(1) rotate(198deg);
198 }
199 100%{
200 opacity:.12;
201 transform:scale(1) rotate(238deg);
202 }
203 }
204
205 @keyframes ringPulse3{
206 0%,22%{
207 opacity:0;
208 transform:scale(.45) rotate(20deg);
209 }
210 32%{
211 opacity:.75;
212 transform:scale(1) rotate(58deg);
213 }
214 48%{
215 opacity:.18;
216 transform:scale(1) rotate(108deg);
217 }
218 70%{
219 opacity:.55;
220 transform:scale(1) rotate(188deg);
221 }
222 88%{
223 opacity:.18;
224 transform:scale(1) rotate(248deg);
225 }
226 100%{
227 opacity:.08;
228 transform:scale(1) rotate(288deg);
229 }
230 }
231
232 @keyframes brushOrbit{
233 0%,12%{
234 opacity:0;
235 transform:rotate(-90deg) translateX(0) scale(.7);
236 }
237 18%{
238 opacity:1;
239 transform:rotate(-58deg) translateX(16px) scale(.95);
240 }
241 34%{
242 opacity:0;
243 transform:rotate(8deg) translateX(16px) scale(.95);
244 }
245 56%{
246 opacity:1;
247 transform:rotate(118deg) translateX(16px) scale(.95);
248 }
249 72%{
250 opacity:0;
251 transform:rotate(182deg) translateX(16px) scale(.95);
252 }
253 100%{
254 opacity:0;
255 transform:rotate(270deg) translateX(16px) scale(.95);
256 }
257 }
258</style>
259</head>
260<body>
261 <div class="stage">
262 <div class="loader" aria-label="loading animation">
263 <div class="seed"></div>
264 <div class="ring r1"></div>
265 <div class="ring r2"></div>
266 <div class="ring r3"></div>
267 <div class="brush"></div>
268 </div>
269 </div>
270</body>
271</html>