← back to Only CSS: Pulse Boxcompare with ground truth →
model output
Claude Sonnet 4.6
Only CSS: Pulse Box
A 0.71T 0.30
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>Neon Geometry Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9 body { background: #000; overflow: hidden; width: 100vw; height: 100vh; }
10 canvas { display: block; }
11</style>
12</head>
13<body>
14<canvas id="c"></canvas>
15<script>
16const canvas = document.getElementById('c');
17const ctx = canvas.getContext('2d');
18canvas.width = window.innerWidth;
19canvas.height = window.innerHeight;
20
21const W = canvas.width, H = canvas.height;
22
23function rand(a, b) { return a + Math.random() * (b - a); }
24function randInt(a, b) { return Math.floor(rand(a, b)); }
25
26// Color palette: green, blue, cyan
27const colors = [
28 '#00ff44', '#00ee33', '#00cc22',
29 '#0044ff', '#0033ee', '#0022cc',
30 '#00ffff', '#00ccff', '#00aaff',
31 '#00ff88', '#00ffaa'
32];
33
34function randColor() {
35 return colors[randInt(0, colors.length)];
36}
37
38function lerpColor(c1, c2, t) {
39 // parse hex
40 const p = c => [parseInt(c.slice(1,3),16), parseInt(c.slice(3,5),16), parseInt(c.slice(5,7),16)];
41 const a = p(c1), b = p(c2);
42 const r = Math.round(a[0]+(b[0]-a[0])*t);
43 const g = Math.round(a[1]+(b[1]-a[1])*t);
44 const bl = Math.round(a[2]+(b[2]-a[2])*t);
45 return `rgb(${r},${g},${bl})`;
46}
47
48class Shape {
49 constructor() {
50 this.reset(true);
51 }
52
53 reset(initial) {
54 this.type = Math.random() < 0.6 ? 'rect' : 'line';
55 this.x = rand(-W*0.5, W*1.5);
56 this.y = rand(-H*0.5, H*1.5);
57 if (initial) {
58 this.x = rand(-W*0.2, W*1.2);
59 this.y = rand(-H*0.2, H*1.2);
60 }
61 this.vx = rand(-3, 3);
62 this.vy = rand(-3, 3);
63 this.angle = rand(0, Math.PI * 2);
64 this.va = rand(-0.02, 0.02);
65 this.color1 = randColor();
66 this.color2 = randColor();
67 this.alpha = 0;
68 this.fadeIn = rand(0.005, 0.02);
69 this.life = rand(200, 600);
70 this.age = initial ? rand(0, 300) : 0;
71 this.maxAlpha = rand(0.5, 1.0);
72
73 if (this.type === 'rect') {
74 this.w = rand(60, 200);
75 this.h = rand(60, 200);
76 this.lineWidth = rand(2, 8);
77 } else {
78 this.len = rand(40, 200);
79 this.lineWidth = rand(1, 4);
80 }
81 }
82
83 update() {
84 this.x += this.vx;
85 this.y += this.vy;
86 this.angle += this.va;
87 this.age++;
88
89 if (this.age < 60) {
90 this.alpha = Math.min(this.maxAlpha, this.alpha + this.fadeIn);
91 } else if (this.age > this.life - 60) {
92 this.alpha = Math.max(0, this.alpha - this.fadeIn);
93 }
94
95 if (this.age > this.life ||
96 (this.x < -W || this.x > W*2 || this.y < -H || this.y > H*2)) {
97 this.reset(false);
98 }
99 }
100
101 draw(ctx) {
102 ctx.save();
103 ctx.globalAlpha = this.alpha;
104 ctx.translate(this.x, this.y);
105 ctx.rotate(this.angle);
106 ctx.lineWidth = this.lineWidth;
107 ctx.strokeStyle = this.color1;
108
109 if (this.type === 'rect') {
110 // gradient stroke
111 const grad = ctx.createLinearGradient(-this.w/2, -this.h/2, this.w/2, this.h/2);
112 grad.addColorStop(0, this.color1);
113 grad.addColorStop(1, this.color2);
114 ctx.strokeStyle = grad;
115 ctx.strokeRect(-this.w/2, -this.h/2, this.w, this.h);
116 } else {
117 const grad = ctx.createLinearGradient(-this.len/2, 0, this.len/2, 0);
118 grad.addColorStop(0, this.color1);
119 grad.addColorStop(1, this.color2);
120 ctx.strokeStyle = grad;
121 ctx.beginPath();
122 ctx.moveTo(-this.len/2, 0);
123 ctx.lineTo(this.len/2, 0);
124 ctx.stroke();
125 }
126 ctx.restore();
127 }
128}
129
130// Also add some "partial rect" shapes (L-shapes, open rectangles)
131class PartialRect {
132 constructor() {
133 this.reset(true);
134 }
135
136 reset(initial) {
137 this.x = rand(-W*0.3, W*1.3);
138 this.y = rand(-H*0.3, H*1.3);
139 if (initial) {
140 this.x = rand(0, W);
141 this.y = rand(0, H);
142 }
143 this.vx = rand(-2.5, 2.5);
144 this.vy = rand(-2.5, 2.5);
145 this.angle = rand(0, Math.PI * 2);
146 this.va = rand(-0.025, 0.025);
147 this.color1 = randColor();
148 this.color2 = randColor();
149 this.alpha = 0;
150 this.fadeIn = rand(0.008, 0.025);
151 this.life = rand(150, 500);
152 this.age = initial ? rand(0, 200) : 0;
153 this.maxAlpha = rand(0.4, 1.0);
154 this.w = rand(50, 180);
155 this.h = rand(50, 180);
156 this.lineWidth = rand(2, 7);
157 // which sides to draw: top, right, bottom, left
158 this.sides = [true, Math.random()>0.3, Math.random()>0.2, Math.random()>0.3];
159 }
160
161 update() {
162 this.x += this.vx;
163 this.y += this.vy;
164 this.angle += this.va;
165 this.age++;
166
167 if (this.age < 60) {
168 this.alpha = Math.min(this.maxAlpha, this.alpha + this.fadeIn);
169 } else if (this.age > this.life - 60) {
170 this.alpha = Math.max(0, this.alpha - this.fadeIn);
171 }
172
173 if (this.age > this.life ||
174 (this.x < -W || this.x > W*2 || this.y < -H || this.y > H*2)) {
175 this.reset(false);
176 }
177 }
178
179 draw(ctx) {
180 ctx.save();
181 ctx.globalAlpha = this.alpha;
182 ctx.translate(this.x, this.y);
183 ctx.rotate(this.angle);
184 ctx.lineWidth = this.lineWidth;
185
186 const hw = this.w/2, hh = this.h/2;
187 const corners = [[-hw,-hh],[hw,-hh],[hw,hh],[-hw,hh]];
188 const sideMap = [[0,1],[1,2],[2,3],[3,0]];
189
190 for (let i = 0; i < 4; i++) {
191 if (!this.sides[i]) continue;
192 const [ai, bi] = sideMap[i];
193 const t = i / 3;
194 ctx.strokeStyle = lerpColor(this.color1, this.color2, t);
195 ctx.beginPath();
196 ctx.moveTo(...corners[ai]);
197 ctx.lineTo(...corners[bi]);
198 ctx.stroke();
199 }
200 ctx.restore();
201 }
202}
203
204const shapes = [];
205const partials = [];
206
207// Initialize shapes
208for (let i = 0; i < 25; i++) shapes.push(new Shape());
209for (let i = 0; i < 20; i++) partials.push(new PartialRect());
210
211let startTime = null;
212
213function animate(ts) {
214 if (!startTime) startTime = ts;
215 const elapsed = ts - startTime;
216
217 // Fade in from black
218 const fadeAlpha = Math.min(1, elapsed / 2000);
219
220 ctx.fillStyle = 'rgba(0,0,0,0.15)';
221 ctx.fillRect(0, 0, W, H);
222
223 // Overall scene alpha
224 ctx.save();
225 ctx.globalAlpha = fadeAlpha;
226
227 for (const s of shapes) {
228 s.update();
229 s.draw(ctx);
230 }
231 for (const p of partials) {
232 p.update();
233 p.draw(ctx);
234 }
235
236 ctx.restore();
237
238 requestAnimationFrame(animate);
239}
240
241// Start with black
242ctx.fillStyle = '#000';
243ctx.fillRect(0, 0, W, H);
244
245requestAnimationFrame(animate);
246
247window.addEventListener('resize', () => {
248 canvas.width = window.innerWidth;
249 canvas.height = window.innerHeight;
250});
251</script>
252</body>
253</html>