← back to Only CSS: Peacockcompare with ground truth →
model output
Claude Sonnet 4.6
Only CSS: Peacock
A 0.48T 0.15
1<!DOCTYPE html>
2<html>
3<head>
4<style>
5* { margin: 0; padding: 0; box-sizing: border-box; }
6body { background: #000; overflow: hidden; width: 100vw; height: 100vh; }
7canvas { display: block; }
8</style>
9</head>
10<body>
11<canvas id="c"></canvas>
12<script>
13const canvas = document.getElementById('c');
14const ctx = canvas.getContext('2d');
15canvas.width = 1024;
16canvas.height = 768;
17
18const cx = 512, cy = 390;
19const startTime = performance.now();
20const duration = 3000;
21
22function hslToRgb(h, s, l) {
23 h = h % 360; if (h < 0) h += 360;
24 s /= 100; l /= 100;
25 const c = (1 - Math.abs(2*l-1)) * s;
26 const x = c * (1 - Math.abs((h/60)%2 - 1));
27 const m = l - c/2;
28 let r=0,g=0,b=0;
29 if(h<60){r=c;g=x;}else if(h<120){r=x;g=c;}else if(h<180){g=c;b=x;}
30 else if(h<240){g=x;b=c;}else if(h<300){r=x;b=c;}else{r=c;b=x;}
31 return `rgb(${Math.round((r+m)*255)},${Math.round((g+m)*255)},${Math.round((b+m)*255)})`;
32}
33
34function drawBlade(ctx, angle, size, color) {
35 ctx.save();
36 ctx.rotate(angle);
37 ctx.fillStyle = color;
38 ctx.beginPath();
39 // Jagged blade shape
40 const r = size;
41 ctx.moveTo(0, 0);
42 const steps = 12;
43 for (let i = 0; i <= steps; i++) {
44 const a = (i / steps) * Math.PI * 0.7 - Math.PI * 0.1;
45 const jag = (i % 2 === 0) ? r : r * 0.75;
46 ctx.lineTo(Math.cos(a) * jag, Math.sin(a) * jag);
47 }
48 ctx.closePath();
49 ctx.fill();
50 ctx.restore();
51}
52
53function drawFrame(t) {
54 ctx.clearRect(0, 0, canvas.width, canvas.height);
55 ctx.fillStyle = '#000';
56 ctx.fillRect(0, 0, canvas.width, canvas.height);
57
58 const progress = Math.min(t / duration, 1);
59
60 // Number of trail copies
61 const maxTrail = 60;
62 const trailCount = Math.floor(progress * maxTrail) + 4;
63
64 // Base rotation speed
65 const totalRotation = progress * Math.PI * 6;
66
67 // Spiral expansion
68 const maxRadius = 350;
69
70 for (let i = 0; i < trailCount; i++) {
71 const frac = i / Math.max(trailCount - 1, 1);
72 const age = 1 - frac; // older = smaller frac index
73
74 // Each copy is offset in rotation and position along spiral
75 const trailFrac = i / maxTrail;
76 const angle = totalRotation - frac * Math.PI * 5;
77
78 // Spiral position
79 const spiralR = trailFrac * maxRadius * progress;
80 const spiralAngle = -frac * Math.PI * 4 + totalRotation * 0.3;
81
82 const px = cx + Math.cos(spiralAngle) * spiralR * progress;
83 const py = cy + Math.sin(spiralAngle) * spiralR * progress;
84
85 // Size: core is bigger, trail gets smaller
86 const size = (1 - frac * 0.6) * (20 + progress * 30);
87
88 // Color: red(0) -> pink -> magenta -> purple -> blue(240)
89 const hue = frac * 240;
90 const sat = 100;
91 const light = 45 + frac * 10;
92 const color = hslToRgb(hue, sat, light);
93
94 const alpha = 0.7 + frac * 0.3;
95
96 ctx.save();
97 ctx.globalAlpha = alpha;
98 ctx.translate(px, py);
99
100 // Draw 4-blade pinwheel
101 for (let b = 0; b < 4; b++) {
102 drawBlade(ctx, angle + b * Math.PI / 2, size, color);
103 }
104
105 // Center circle
106 ctx.beginPath();
107 ctx.arc(0, 0, size * 0.18, 0, Math.PI * 2);
108 ctx.fillStyle = '#000';
109 ctx.fill();
110
111 ctx.restore();
112 }
113}
114
115function animate(now) {
116 const t = now - startTime;
117 drawFrame(t);
118 if (t < duration + 500) {
119 requestAnimationFrame(animate);
120 }
121}
122
123requestAnimationFrame(animate);
124</script>
125</body>
126</html>