← back to Only CSS: Screw 2compare with ground truth →
model output
Claude Sonnet 4.6
Only CSS: Screw 2
A 0.57T 0.19
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>SCREW Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 background-color: #1e2a35;
12 display: flex;
13 align-items: center;
14 justify-content: center;
15 width: 100vw;
16 height: 100vh;
17 overflow: hidden;
18 }
19
20 .scene {
21 position: relative;
22 width: 500px;
23 height: 160px;
24 display: flex;
25 align-items: center;
26 justify-content: center;
27 }
28
29 canvas {
30 position: absolute;
31 top: 50%;
32 left: 50%;
33 transform: translate(-50%, -50%);
34 }
35</style>
36</head>
37<body>
38<canvas id="c"></canvas>
39<script>
40const canvas = document.getElementById('c');
41const ctx = canvas.getContext('2d');
42canvas.width = 1024;
43canvas.height = 576;
44
45const CX = canvas.width / 2;
46const CY = canvas.height / 2;
47
48// Animation state
49let t = 0;
50const PERIOD = 4000; // ms per cycle
51
52// Letter definitions for SCREW
53// Each letter is drawn as a bold, condensed uppercase letter
54// We'll use canvas text rendering with a custom font stack
55
56function drawVinyl(ctx, x, y, r, angle) {
57 ctx.save();
58 ctx.translate(x, y);
59 ctx.rotate(angle);
60
61 // Outer ring
62 ctx.beginPath();
63 ctx.arc(0, 0, r, 0, Math.PI * 2);
64 ctx.fillStyle = 'white';
65 ctx.fill();
66
67 // Grooves (dark rings)
68 const grooves = [0.85, 0.72, 0.60, 0.48, 0.36];
69 grooves.forEach(f => {
70 ctx.beginPath();
71 ctx.arc(0, 0, r * f, 0, Math.PI * 2);
72 ctx.fillStyle = '#1e2a35';
73 ctx.fill();
74 ctx.beginPath();
75 ctx.arc(0, 0, r * (f - 0.06), 0, Math.PI * 2);
76 ctx.fillStyle = 'white';
77 ctx.fill();
78 });
79
80 // Label circle
81 ctx.beginPath();
82 ctx.arc(0, 0, r * 0.28, 0, Math.PI * 2);
83 ctx.fillStyle = '#1e2a35';
84 ctx.fill();
85
86 // Center hole
87 ctx.beginPath();
88 ctx.arc(0, 0, r * 0.06, 0, Math.PI * 2);
89 ctx.fillStyle = 'white';
90 ctx.fill();
91
92 ctx.restore();
93}
94
95// Letter fragment system
96// Each letter has segments that can rotate around center
97class LetterFragment {
98 constructor(letter, baseX, baseY, segIndex, totalSegs) {
99 this.letter = letter;
100 this.baseX = baseX;
101 this.baseY = baseY;
102 this.segIndex = segIndex;
103 this.totalSegs = totalSegs;
104 this.angle = (segIndex / totalSegs) * Math.PI * 2;
105 this.radius = 0;
106 this.rotOffset = (segIndex / totalSegs) * Math.PI * 2;
107 }
108}
109
110// Draw a single letter with horizontal stripe effect (vinyl groove look)
111function drawStripedLetter(ctx, letter, x, y, size, stripeAngle, alpha) {
112 ctx.save();
113 ctx.globalAlpha = alpha;
114 ctx.translate(x, y);
115
116 // Clip to letter shape
117 ctx.save();
118 ctx.font = `bold ${size}px Arial Black, Impact, sans-serif`;
119 ctx.textAlign = 'center';
120 ctx.textBaseline = 'middle';
121
122 // Draw full letter white
123 ctx.fillStyle = 'white';
124 ctx.fillText(letter, 0, 0);
125
126 // Draw dark stripes over it
127 ctx.globalCompositeOperation = 'destination-out';
128 const stripeCount = 8;
129 const stripeWidth = size / (stripeCount * 2);
130
131 for (let i = 0; i < stripeCount; i++) {
132 const sy = -size * 0.6 + i * (size * 1.2 / stripeCount) + stripeWidth * 0.5;
133 ctx.fillStyle = 'rgba(0,0,0,1)';
134 ctx.fillRect(-size, sy, size * 2, stripeWidth * 0.7);
135 }
136
137 ctx.restore();
138 ctx.restore();
139}
140
141// Segments of each letter that spin outward
142function getLetterSegments(letter, x, y, fontSize) {
143 // Return array of {clipY, clipH, letter, x, y}
144 const segs = [];
145 const n = 7;
146 const h = fontSize * 1.2;
147 for (let i = 0; i < n; i++) {
148 segs.push({
149 letter, x, y,
150 clipY: y - h/2 + i * (h/n),
151 clipH: h/n,
152 index: i,
153 total: n
154 });
155 }
156 return segs;
157}
158
159const letters = ['S', 'C', 'R', 'E', 'W'];
160const fontSize = 110;
161const letterSpacing = 95;
162const totalWidth = (letters.length - 1) * letterSpacing;
163const startX = CX - totalWidth / 2;
164
165// Precompute letter positions
166const letterPositions = letters.map((l, i) => ({
167 letter: l,
168 x: startX + i * letterSpacing,
169 y: CY
170}));
171
172// Vinyl center position (between C and R, roughly center)
173const vinylX = CX + 5;
174const vinylY = CY;
175const vinylR = 52;
176
177let vinylAngle = 0;
178
179function easeInOut(t) {
180 return t < 0.5 ? 2*t*t : -1+(4-2*t)*t;
181}
182
183function easeIn(t) {
184 return t * t * t;
185}
186
187function easeOut(t) {
188 return 1 - Math.pow(1-t, 3);
189}
190
191function draw(timestamp) {
192 ctx.clearRect(0, 0, canvas.width, canvas.height);
193 ctx.fillStyle = '#1e2a35';
194 ctx.fillRect(0, 0, canvas.width, canvas.height);
195
196 const phase = (timestamp % PERIOD) / PERIOD; // 0 to 1
197
198 // Animation phases:
199 // 0.0 - 0.15: normal display
200 // 0.15 - 0.45: spin out (letters fragment and rotate)
201 // 0.45 - 0.55: fully spun (vortex)
202 // 0.55 - 0.85: spin back in
203 // 0.85 - 1.0: normal display
204
205 let spinAmount = 0;
206 let scaleAmount = 1;
207
208 if (phase < 0.15) {
209 spinAmount = 0;
210 scaleAmount = 1;
211 } else if (phase < 0.45) {
212 const p = (phase - 0.15) / 0.30;
213 spinAmount = easeIn(p);
214 scaleAmount = 1;
215 } else if (phase < 0.55) {
216 spinAmount = 1;
217 scaleAmount = 1;
218 } else if (phase < 0.85) {
219 const p = (phase - 0.55) / 0.30;
220 spinAmount = 1 - easeOut(p);
221 scaleAmount = 1;
222 } else {
223 spinAmount = 0;
224 scaleAmount = 1;
225 }
226
227 // Vinyl rotation speed
228 vinylAngle = timestamp * 0.003 * (1 + spinAmount * 5);
229
230 const maxRadius = 200;
231 const maxRotation = Math.PI * 3;
232
233 ctx.save();
234 ctx.font = `bold ${fontSize}px Arial Black, Impact, sans-serif`;
235 ctx.textAlign = 'center';
236 ctx.textBaseline = 'middle';
237
238 // Draw each letter's segments
239 letterPositions.forEach((lp, li) => {
240 const n = 7;
241 const h = fontSize * 1.2;
242 const segH = h / n;
243
244 for (let si = 0; si < n; si++) {
245 const segCenterY = lp.y - h/2 + si * segH + segH/2;
246
247 // Calculate spin for this segment
248 // Segments further from center spin more
249 const distFromCenter = Math.sqrt(
250 Math.pow(lp.x - vinylX, 2) + Math.pow(segCenterY - vinylY, 2)
251 );
252
253 const angle = Math.atan2(segCenterY - vinylY, lp.x - vinylX);
254
255 // Spin: rotate around vinyl center
256 const spinRot = spinAmount * maxRotation * (0.5 + distFromCenter / 300);
257 const spinRadius = spinAmount * distFromCenter * 0.8;
258
259 const newAngle = angle + spinRot;
260 const newDist = distFromCenter + spinRadius * spinAmount;
261
262 const dx = spinAmount > 0 ?
263 (Math.cos(newAngle) * newDist - (lp.x - vinylX)) : 0;
264 const dy = spinAmount > 0 ?
265 (Math.sin(newAngle) * newDist - (segCenterY - vinylY)) : 0;
266
267 ctx.save();
268 ctx.translate(lp.x + dx, segCenterY + dy);
269 ctx.rotate(spinRot * 0.5);
270
271 // Clip to segment strip
272 ctx.beginPath();
273 ctx.rect(-fontSize, -segH/2, fontSize * 2, segH);
274 ctx.clip();
275
276 ctx.translate(0, -(segCenterY - lp.y));
277
278 // Draw letter white
279 ctx.fillStyle = 'white';
280 ctx.fillText(lp.letter, 0, 0);
281
282 // Draw dark stripes
283 ctx.globalCompositeOperation = 'destination-out';
284 const stripeCount = 7;
285 const sw = segH * 0.35;
286 for (let k = 0; k < stripeCount; k++) {
287 const sy2 = -h/2 + k * (h/stripeCount) + sw * 0.3;
288 ctx.fillStyle = 'rgba(0,0,0,1)';
289 ctx.fillRect(-fontSize, sy2, fontSize * 2, sw);
290 }
291 ctx.globalCompositeOperation = 'source-over';
292
293 ctx.restore();
294 }
295 });
296
297 // Draw vinyl record on top
298 const vinylScale = 1 - spinAmount * 0.3 + spinAmount * 0.3;
299 ctx.save();
300 ctx.translate(vinylX, vinylY);
301 ctx.scale(vinylScale, vinylScale);
302 ctx.rotate(vinylAngle);
303
304 // Outer vinyl
305 ctx.beginPath();
306 ctx.arc(0, 0, vinylR, 0, Math.PI * 2);
307 ctx.fillStyle = 'white';
308 ctx.fill();
309
310 // Grooves
311 const grooveRatios = [0.88, 0.76, 0.64, 0.52, 0.40];
312 grooveRatios.forEach(f => {
313 ctx.beginPath();
314 ctx.arc(0, 0, vinylR * f, 0, Math.PI * 2);
315 ctx.fillStyle = '#1e2a35';
316 ctx.fill();
317 ctx.beginPath();
318 ctx.arc(0, 0, vinylR * (f - 0.07), 0, Math.PI * 2);
319 ctx.fillStyle = 'white';
320 ctx.fill();
321 });
322
323 // Label
324 ctx.beginPath();
325 ctx.arc(0, 0, vinylR * 0.30, 0, Math.PI * 2);
326 ctx.fillStyle = '#1e2a35';
327 ctx.fill();
328
329 // Center hole
330 ctx.beginPath();
331 ctx.arc(0, 0, vinylR * 0.07, 0, Math.PI * 2);
332 ctx.fillStyle = 'white';
333 ctx.fill();
334
335 ctx.restore();
336 ctx.restore();
337
338 requestAnimationFrame(draw);
339}
340
341requestAnimationFrame(draw);
342</script>
343</body>
344</html>