← back to Thinking about SVG-preloaderscompare with ground truth →
model output
GPT-5.4
Thinking about SVG-preloaders
A 0.62T 0.35
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>Rotating Ring Trail</title>
7<style>
8 html, body {
9 margin: 0;
10 width: 100%;
11 height: 100%;
12 overflow: hidden;
13 background:
14 linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.035) 75%, rgba(255,255,255,.035)),
15 linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.035) 75%, rgba(255,255,255,.035));
16 background-color: #020304;
17 background-size: 18px 18px;
18 background-position: 0 0, 9px 9px;
19 }
20
21 body {
22 display: grid;
23 place-items: center;
24 }
25
26 canvas {
27 display: block;
28 width: 100vw;
29 height: 100vh;
30 }
31</style>
32</head>
33<body>
34<canvas id="c"></canvas>
35
36<script>
37const canvas = document.getElementById('c');
38const ctx = canvas.getContext('2d');
39
40function resize() {
41 const dpr = Math.min(devicePixelRatio || 1, 2);
42 canvas.width = innerWidth * dpr;
43 canvas.height = innerHeight * dpr;
44 canvas.style.width = innerWidth + 'px';
45 canvas.style.height = innerHeight + 'px';
46 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
47}
48addEventListener('resize', resize);
49resize();
50
51const COUNT = 12;
52const R = 40;
53const DOT_R = 9;
54const SPEED = Math.PI * 0.72; // ~8.7s per revolution
55const spacing = 0.22; // radians between dots
56
57function ringColor(t) {
58 // t: 0 = head, 1 = tail
59 const hue = 52 - t * 42; // yellow -> orange/red
60 const sat = 92 - t * 18;
61 const light = 56 - t * 26;
62 const alpha = 0.95 - t * 0.82;
63 return `hsla(${hue}, ${sat}%, ${light}%, ${alpha})`;
64}
65
66function fillColor(t) {
67 const hue = 42 - t * 28;
68 const sat = 88 - t * 20;
69 const light = 50 - t * 22;
70 const alpha = 0.22 - t * 0.18;
71 return `hsla(${hue}, ${sat}%, ${light}%, ${Math.max(alpha, 0)})`;
72}
73
74function draw(now) {
75 now *= 0.001;
76
77 ctx.clearRect(0, 0, innerWidth, innerHeight);
78
79 const cx = innerWidth * 0.5;
80 const cy = innerHeight * 0.37;
81
82 // slight vertical ellipse like the reference
83 const rx = R;
84 const ry = R * 0.98;
85
86 // orient so the bright head sits around upper-right and rotates clockwise
87 const base = -Math.PI * 0.15 + now * SPEED;
88
89 for (let i = COUNT - 1; i >= 0; i--) {
90 const t = i / (COUNT - 1);
91 const a = base - i * spacing;
92
93 const x = cx + Math.cos(a) * rx;
94 const y = cy + Math.sin(a) * ry;
95
96 const r = DOT_R + (1 - t) * 1.2;
97
98 // soft glow
99 const glow = ctx.createRadialGradient(x, y, 0, x, y, r * 2.2);
100 glow.addColorStop(0, fillColor(t));
101 glow.addColorStop(1, 'rgba(0,0,0,0)');
102 ctx.fillStyle = glow;
103 ctx.beginPath();
104 ctx.arc(x, y, r * 2.2, 0, Math.PI * 2);
105 ctx.fill();
106
107 // some trail dots are filled, head dots are hollow rings
108 const hollowMix = t < 0.42;
109 if (!hollowMix || t > 0.75) {
110 ctx.fillStyle = fillColor(t * 0.9);
111 ctx.beginPath();
112 ctx.arc(x, y, r * 0.95, 0, Math.PI * 2);
113 ctx.fill();
114 }
115
116 ctx.strokeStyle = ringColor(t);
117 ctx.lineWidth = 3;
118 ctx.beginPath();
119 ctx.arc(x, y, r * 0.78, 0, Math.PI * 2);
120 ctx.stroke();
121 }
122
123 requestAnimationFrame(draw);
124}
125
126requestAnimationFrame(draw);
127</script>
128</body>
129</html>