1Looking at the animation, I can see:
2- A pinwheel/star shape with 6 arms, each made of 3 elongated oval segments
3- The shape alternates between "open" (arms spread out like a starfish) and "closed" (compact pinwheel)
4- Colors gradient from pink/magenta on the left to blue/indigo on the right, with purple in between
5- The whole shape slowly rotates
6- There are faint spiral trail lines visible during transitions
7- The animation cycles with easing (ease-in-out)
8
9Each arm has 3 segments: a large inner oval, medium middle oval, and small outer oval, with the outer ones at an angle (like a bent arm).
10
11<!DOCTYPE html>
12<html>
13<head>
14<style>
15* { margin: 0; padding: 0; box-sizing: border-box; }
16body {
17 width: 100vw; height: 100vh;
18 background: radial-gradient(ellipse at 70% 40%, #d8d8e8 0%, #e8e8f0 40%, #f0f0f5 100%);
19 display: flex; align-items: center; justify-content: center;
20 overflow: hidden;
21}
22canvas { display: block; }
23</style>
24</head>
25<body>
26<canvas id="c"></canvas>
27<script>
28const canvas = document.getElementById('c');
29const ctx = canvas.getContext('2d');
30canvas.width = window.innerWidth;
31canvas.height = window.innerHeight;
32
33const cx = canvas.width / 2 - 20;
34const cy = canvas.height / 2 + 10;
35
36// 6 arms, each with 3 segments
37// Colors: arm 0=pink, arm 1=magenta, arm 2=purple-pink, arm 3=purple, arm 4=blue-purple, arm 5=blue
38const armColors = [
39 '#e040a0', // hot pink
40 '#cc3399', // magenta
41 '#9933cc', // purple-pink
42 '#7744cc', // purple
43 '#5544dd', // blue-purple
44 '#4444cc', // blue-indigo
45];
46
47// Each arm: base angle, 3 segments
48// In "open" state: segments are spread out (like starfish arms)
49// In "closed" state: segments form compact pinwheel petals
50
51function lerp(a, b, t) { return a + (b - a) * t; }
52
53function easeInOut(t) {
54 return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
55}
56
57// Draw an elongated oval (ellipse) at position, with given angle and size
58function drawSegment(x, y, angle, w, h, color, alpha) {
59 ctx.save();
60 ctx.globalAlpha = alpha;
61 ctx.translate(x, y);
62 ctx.rotate(angle);
63 ctx.beginPath();
64 ctx.ellipse(0, 0, w, h, 0, 0, Math.PI * 2);
65 ctx.fillStyle = color;
66 ctx.shadowColor = 'rgba(0,0,0,0.15)';
67 ctx.shadowBlur = 6;
68 ctx.fill();
69 ctx.restore();
70}
71
72// Arm definition: 3 segments per arm
73// Segment: {dist from center, angle offset from arm direction, own rotation, w, h}
74// "open" = arms extended outward with bent tips
75// "closed" = compact pinwheel
76
77const NUM_ARMS = 6;
78const BASE_ANGLES_OPEN = Array.from({length: NUM_ARMS}, (_, i) => i * Math.PI * 2 / NUM_ARMS);
79
80// Segment sizes
81const SEG = [
82 {w: 14, h: 48}, // inner large
83 {w: 10, h: 32}, // middle
84 {w: 6, h: 20}, // outer small
85];
86
87// Open state: segments along arm direction, spread out
88function getOpenSegments(armAngle) {
89 // inner segment: close to center, along arm
90 // middle: further out
91 // outer: even further, slightly bent
92 return [
93 { dist: 55, angleOff: 0, rotOff: 0, ...SEG[0] },
94 { dist: 110, angleOff: 0, rotOff: 0, ...SEG[1] },
95 { dist: 155, angleOff: 0.3, rotOff: 0.3, ...SEG[2] },
96 ];
97}
98
99// Closed state: compact pinwheel - segments rotated ~45deg from arm, closer
100function getClosedSegments(armAngle) {
101 return [
102 { dist: 45, angleOff: -0.5, rotOff: -0.5, ...SEG[0] },
103 { dist: 85, angleOff: -0.3, rotOff: -0.3, ...SEG[1] },
104 { dist: 118, angleOff: 0.2, rotOff: 0.2, ...SEG[2] },
105 ];
106}
107
108let startTime = null;
109const CYCLE = 3000; // ms per cycle
110const OPEN_HOLD = 0.3;
111const CLOSE_HOLD = 0.3;
112const TRANS = 0.2;
113
114// Global rotation speed
115const ROT_SPEED = 0.0003; // rad/ms
116
117function getPhaseAndT(elapsed) {
118 const t = (elapsed % CYCLE) / CYCLE;
119 // 0 -> OPEN_HOLD: open hold
120 // OPEN_HOLD -> OPEN_HOLD+TRANS: open->close
121 // OPEN_HOLD+TRANS -> OPEN_HOLD+TRANS+CLOSE_HOLD: close hold
122 // rest: close->open
123 const p1 = OPEN_HOLD;
124 const p2 = p1 + TRANS;
125 const p3 = p2 + CLOSE_HOLD;
126 const p4 = p3 + TRANS;
127
128 if (t < p1) return { phase: 'open', blend: 0 };
129 if (t < p2) return { phase: 'closing', blend: easeInOut((t - p1) / TRANS) };
130 if (t < p3) return { phase: 'closed', blend: 1 };
131 if (t < p4) return { phase: 'opening', blend: easeInOut(1 - (t - p3) / TRANS) };
132 return { phase: 'open', blend: 0 };
133}
134
135function drawArm(armIdx, globalAngle, blend) {
136 const baseAngle = BASE_ANGLES_OPEN[armIdx] + globalAngle;
137 const color = armColors[armIdx];
138
139 const openSegs = getOpenSegments(baseAngle);
140 const closedSegs = getClosedSegments(baseAngle);
141
142 for (let s = 0; s < 3; s++) {
143 const os = openSegs[s];
144 const cs = closedSegs[s];
145
146 const dist = lerp(os.dist, cs.dist, blend);
147 const angleOff = lerp(os.angleOff, cs.angleOff, blend);
148 const rotOff = lerp(os.rotOff, cs.rotOff, blend);
149 const w = lerp(os.w, cs.w, blend);
150 const h = lerp(os.h, cs.h, blend);
151
152 const posAngle = baseAngle + angleOff;
153 const x = cx + Math.cos(posAngle) * dist;
154 const y = cy + Math.sin(posAngle) * dist;
155 const rot = baseAngle + rotOff + Math.PI / 2;
156
157 // Alpha: outer segments fade slightly
158 const alpha = s === 2 ? 0.85 : 1.0;
159 drawSegment(x, y, rot, w, h, color, alpha);
160 }
161}
162
163// Draw faint spiral trails during transitions
164function drawTrails(blend, globalAngle) {
165 if (blend < 0.05 || blend > 0.95) return;
166 const trailAlpha = Math.sin(blend * Math.PI) * 0.12;
167 ctx.save();
168 ctx.globalAlpha = trailAlpha;
169 ctx.strokeStyle = '#8866cc';
170 ctx.lineWidth = 1;
171
172 for (let i = 0; i < 2; i++) {
173 ctx.beginPath();
174 const startA = globalAngle + i * Math.PI;
175 for (let a = 0; a < Math.PI * 2; a += 0.05) {
176 const r = 80 + a * 25;
177 const x = cx + Math.cos(startA + a) * r;
178 const y = cy + Math.sin(startA + a) * r;
179 if (a === 0) ctx.moveTo(x, y);
180 else ctx.lineTo(x, y);
181 }
182 ctx.stroke();
183 }
184 ctx.restore();
185}
186
187function draw(ts) {
188 if (!startTime) startTime = ts;
189 const elapsed = ts - startTime;
190
191 ctx.clearRect(0, 0, canvas.width, canvas.height);
192
193 // Background
194 const grad = ctx.createRadialGradient(canvas.width*0.7, canvas.height*0.35, 0, canvas.width*0.5, canvas.height*0.5, canvas.width*0.7);
195 grad.addColorStop(0, '#d5d5e5');
196 grad.addColorStop(0.4, '#e5e5ee');
197 grad.addColorStop(1, '#eeeeF4');
198 ctx.fillStyle = grad;
199 ctx.fillRect(0, 0, canvas.width, canvas.height);
200
201 const globalAngle = elapsed * ROT_SPEED;
202 const { blend } = getPhaseAndT(elapsed);
203
204 drawTrails(blend, globalAngle);
205
206 for (let i = 0; i < NUM_ARMS; i++) {
207 drawArm(i, globalAngle, blend);
208 }
209
210 requestAnimationFrame(draw);
211}
212
213requestAnimationFrame(draw);
214</script>
215</body>
216</html>