← back to Retro Sine Wave 〰compare with ground truth →
model output
Claude Sonnet 4.6
Retro Sine Wave 〰
A 0.90T 0.17
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>Glitch Lines Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 width: 100vw;
12 height: 100vh;
13 background: radial-gradient(ellipse at 40% 40%, #2a2640 0%, #1e1b2e 50%, #181525 100%);
14 overflow: hidden;
15 display: flex;
16 align-items: center;
17 justify-content: center;
18 }
19
20 canvas {
21 display: block;
22 }
23</style>
24</head>
25<body>
26<canvas id="c"></canvas>
27<script>
28const canvas = document.getElementById('c');
29const ctx = canvas.getContext('2d');
30
31canvas.width = window.innerWidth;
32canvas.height = window.innerHeight;
33
34const W = canvas.width;
35const H = canvas.height;
36
37// Center of the shape
38const cx = W * 0.44;
39const cy = H * 0.46;
40
41// Shape radius (roughly circular/oval)
42const rx = 110;
43const ry = 130;
44
45const NUM_ROWS = 22;
46const ROW_SPACING = 13;
47
48// Each line has: baseX (left anchor), baseWidth, row index
49// The shape is roughly circular, lines span the circle width at each row
50// There are two "groups" - main body and a secondary offset group (the glitch effect)
51
52function getCircleWidth(row, totalRows) {
53 const t = (row / (totalRows - 1)) * 2 - 1; // -1 to 1
54 const w = Math.sqrt(Math.max(0, 1 - t * t));
55 return w;
56}
57
58// Lines data: each line has a "home" position and animates with offset
59class Line {
60 constructor(row, xOffset, width, opacity, delay, speed) {
61 this.row = row;
62 this.baseX = xOffset;
63 this.baseWidth = width;
64 this.opacity = opacity;
65 this.delay = delay;
66 this.speed = speed;
67 this.offset = 0;
68 this.phase = Math.random() * Math.PI * 2;
69 }
70}
71
72const lines = [];
73
74// Build lines based on the circular shape
75for (let i = 0; i < NUM_ROWS; i++) {
76 const t = (i / (NUM_ROWS - 1)) * 2 - 1;
77 const circW = Math.sqrt(Math.max(0, 1 - t * t));
78 const rowY = cy - ry + (i / (NUM_ROWS - 1)) * ry * 2;
79
80 // Main line
81 const mainW = circW * rx * 2 * (0.7 + Math.random() * 0.5);
82 const mainX = cx - circW * rx + (Math.random() - 0.5) * 20;
83 const op = 0.5 + circW * 0.5;
84
85 lines.push({
86 row: i,
87 y: rowY,
88 x: mainX,
89 width: mainW,
90 opacity: op,
91 animOffset: 0,
92 phase: Math.random() * Math.PI * 2,
93 speed: 0.3 + Math.random() * 0.4,
94 isSecondary: false
95 });
96
97 // Secondary "ghost" line (the glitch offset group)
98 if (i > 3 && i < NUM_ROWS - 2) {
99 const secW = circW * rx * (0.4 + Math.random() * 0.6);
100 const secX = cx + circW * rx * 0.1 + Math.random() * 20;
101 lines.push({
102 row: i,
103 y: rowY,
104 x: secX,
105 width: secW,
106 opacity: op * 0.8,
107 animOffset: 0,
108 phase: Math.random() * Math.PI * 2 + 1,
109 speed: 0.4 + Math.random() * 0.5,
110 isSecondary: true
111 });
112 }
113}
114
115let time = 0;
116
117function drawBackground() {
118 const grad = ctx.createRadialGradient(W*0.4, H*0.4, 0, W*0.5, H*0.5, W*0.7);
119 grad.addColorStop(0, '#2d2845');
120 grad.addColorStop(0.5, '#1e1b30');
121 grad.addColorStop(1, '#161320');
122 ctx.fillStyle = grad;
123 ctx.fillRect(0, 0, W, H);
124}
125
126function drawLine(x, y, width, opacity, glowColor) {
127 if (width <= 0) return;
128
129 // Glow effect
130 ctx.save();
131 ctx.globalAlpha = opacity * 0.3;
132 ctx.shadowColor = glowColor;
133 ctx.shadowBlur = 8;
134 ctx.strokeStyle = glowColor;
135 ctx.lineWidth = 3;
136 ctx.beginPath();
137 ctx.moveTo(x, y);
138 ctx.lineTo(x + width, y);
139 ctx.stroke();
140
141 // Core bright line
142 ctx.globalAlpha = opacity;
143 ctx.shadowBlur = 4;
144 ctx.lineWidth = 1.5;
145 ctx.strokeStyle = '#ff6040';
146 ctx.beginPath();
147 ctx.moveTo(x, y);
148 ctx.lineTo(x + width, y);
149 ctx.stroke();
150 ctx.restore();
151}
152
153function animate() {
154 time += 0.016;
155
156 drawBackground();
157
158 // Ambient glow behind lines
159 const glowGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, 160);
160 glowGrad.addColorStop(0, 'rgba(180, 60, 30, 0.12)');
161 glowGrad.addColorStop(1, 'rgba(180, 60, 30, 0)');
162 ctx.fillStyle = glowGrad;
163 ctx.fillRect(0, 0, W, H);
164
165 // The secondary group offset - oscillates back and forth
166 // Looking at frames: secondary group moves right then comes back, cycling
167 const secGroupOffset = Math.sin(time * 0.8) * 40 + Math.cos(time * 0.5) * 20;
168
169 for (const line of lines) {
170 // Individual line animation - slight horizontal drift
171 const indivOffset = Math.sin(time * line.speed + line.phase) * 8;
172
173 let x = line.x + indivOffset;
174 let w = line.width;
175 let op = line.opacity;
176
177 if (line.isSecondary) {
178 x += secGroupOffset;
179 // Secondary lines fade in/out
180 op *= (0.5 + 0.5 * Math.sin(time * 0.6 + line.phase));
181 }
182
183 // Color varies slightly - brighter in center rows
184 const t = (line.row / (NUM_ROWS - 1)) * 2 - 1;
185 const brightness = 1 - Math.abs(t) * 0.4;
186 const r = Math.floor(220 * brightness + 35);
187 const g = Math.floor(60 * brightness);
188 const b = Math.floor(20 * brightness);
189 const color = `rgb(${r},${g},${b})`;
190
191 drawLine(x, line.y, w, op * 0.9, color);
192 }
193
194 requestAnimationFrame(animate);
195}
196
197animate();
198
199window.addEventListener('resize', () => {
200 canvas.width = window.innerWidth;
201 canvas.height = window.innerHeight;
202});
203</script>
204</body>
205</html>