← back to Simple loading indicatorscompare with ground truth →
model output
Claude Sonnet 4.6
Simple loading indicators
A 0.82T 0.29
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>Circle Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 background: white;
12 width: 100vw;
13 height: 100vh;
14 }
15
16 .circle-container {
17 position: absolute;
18 top: 28px;
19 left: 28px;
20 width: 50px;
21 height: 50px;
22 border-radius: 50%;
23 overflow: hidden;
24 }
25
26 .circle-bg {
27 position: absolute;
28 width: 100%;
29 height: 100%;
30 border-radius: 50%;
31 background: #c8c8c8;
32 }
33
34 .circle-fill {
35 position: absolute;
36 width: 100%;
37 height: 100%;
38 border-radius: 50%;
39 background: conic-gradient(#2baa8a 0deg, #2baa8a var(--angle), transparent var(--angle));
40 animation: fillSweep 2s linear infinite;
41 }
42
43 @keyframes fillSweep {
44 0% { --angle: 0deg; }
45 50% { --angle: 360deg; }
46 100% { --angle: 360deg; }
47 }
48
49 .circle-unfill {
50 position: absolute;
51 width: 100%;
52 height: 100%;
53 border-radius: 50%;
54 background: conic-gradient(#c8c8c8 0deg, #c8c8c8 var(--angle2), transparent var(--angle2));
55 animation: unfillSweep 2s linear infinite;
56 }
57
58 @keyframes unfillSweep {
59 0% { --angle2: 0deg; }
60 50% { --angle2: 0deg; }
61 100% { --angle2: 360deg; }
62 }
63</style>
64</head>
65<body>
66
67<div class="circle-container">
68 <div class="circle-bg"></div>
69 <canvas id="c" width="50" height="50" style="position:absolute;top:0;left:0;border-radius:50%;"></canvas>
70</div>
71
72<script>
73 const canvas = document.getElementById('c');
74 const ctx = canvas.getContext('2d');
75 const size = 50;
76 const cx = size / 2;
77 const cy = size / 2;
78 const r = size / 2;
79 const teal = '#2baa8a';
80 const gray = '#c8c8c8';
81 const duration = 2000; // ms for full cycle
82
83 function draw(t) {
84 ctx.clearRect(0, 0, size, size);
85
86 const progress = (t % duration) / duration; // 0 to 1
87
88 // Phase 1: 0 to 0.5 — teal sweeps in (0 to 360)
89 // Phase 2: 0.5 to 1 — gray sweeps in (0 to 360), covering teal
90
91 let tealAngle, grayAngle;
92
93 if (progress < 0.5) {
94 // Teal filling in
95 tealAngle = (progress / 0.5) * Math.PI * 2;
96 grayAngle = 0;
97 } else {
98 // Gray filling back in
99 tealAngle = Math.PI * 2;
100 grayAngle = ((progress - 0.5) / 0.5) * Math.PI * 2;
101 }
102
103 // Draw gray base
104 ctx.beginPath();
105 ctx.arc(cx, cy, r, 0, Math.PI * 2);
106 ctx.fillStyle = gray;
107 ctx.fill();
108
109 // Draw teal sector
110 if (tealAngle > 0) {
111 ctx.beginPath();
112 ctx.moveTo(cx, cy);
113 ctx.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + tealAngle);
114 ctx.closePath();
115 ctx.fillStyle = teal;
116 ctx.fill();
117 }
118
119 // Draw gray sector on top (for phase 2)
120 if (grayAngle > 0) {
121 ctx.beginPath();
122 ctx.moveTo(cx, cy);
123 ctx.arc(cx, cy, r, -Math.PI / 2, -Math.PI / 2 + grayAngle);
124 ctx.closePath();
125 ctx.fillStyle = gray;
126 ctx.fill();
127 }
128 }
129
130 function animate(timestamp) {
131 draw(timestamp);
132 requestAnimationFrame(animate);
133 }
134
135 requestAnimationFrame(animate);
136</script>
137
138</body>
139</html>