← back to Retro Sine Wave 〰compare with ground truth →
model output
GPT-5.4
Retro Sine Wave 〰
A 0.81T 0.24
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" />
6<title>Striped Morph Animation</title>
7<style>
8 :root{
9 --bg1:#2b2740;
10 --bg2:#4a4568;
11 --line:#ff7a5f;
12 --glow:rgba(255,122,95,.55);
13 }
14
15 html,body{
16 margin:0;
17 width:100%;
18 height:100%;
19 overflow:hidden;
20 background:
21 radial-gradient(1200px 900px at 100% 0%, #5a547b 0%, #433e5f 28%, #312c49 58%, #26223a 100%);
22 }
23
24 body{
25 display:grid;
26 place-items:center;
27 font-family:system-ui,sans-serif;
28 }
29
30 .scene{
31 position:relative;
32 width:min(62vmin,520px);
33 aspect-ratio:1/1;
34 filter: saturate(1.05);
35 }
36
37 .shape{
38 position:absolute;
39 inset:0;
40 display:block;
41 filter: drop-shadow(0 0 6px rgba(255,120,90,.18));
42 }
43
44 .line{
45 position:absolute;
46 left:50%;
47 top:50%;
48 height:2px;
49 border-radius:999px;
50 background:linear-gradient(90deg,
51 rgba(255,122,95,.18) 0%,
52 rgba(255,122,95,.95) 18%,
53 #ff7a5f 50%,
54 rgba(255,122,95,.95) 82%,
55 rgba(255,122,95,.18) 100%);
56 box-shadow:
57 0 0 6px var(--glow),
58 0 0 12px rgba(255,122,95,.18);
59 transform-origin:center center;
60 opacity:.96;
61 animation: morph 4.8s cubic-bezier(.55,.02,.35,1) infinite;
62 will-change: transform, width;
63 }
64
65 @keyframes morph{
66 0%,100%{
67 transform:translate(calc(var(--x0) * 1px), calc(var(--y) * 1px));
68 width:calc(var(--w0) * 1px);
69 }
70 50%{
71 transform:translate(calc(var(--x1) * 1px), calc(var(--y) * 1px));
72 width:calc(var(--w1) * 1px);
73 }
74 }
75
76 /* subtle overall breathing like the reference softness */
77 .shape{
78 animation: drift 4.8s cubic-bezier(.55,.02,.35,1) infinite;
79 }
80 @keyframes drift{
81 0%,100%{ transform:translateX(-8px); }
82 50%{ transform:translateX(10px); }
83 }
84</style>
85</head>
86<body>
87 <div class="scene">
88 <div class="shape" id="shape"></div>
89 </div>
90
91<script>
92(() => {
93 const shape = document.getElementById('shape');
94
95 // Horizontal scanlines approximating the striped morph seen in the frames:
96 // from a "b"-like silhouette to a "p"-like silhouette.
97 const count = 26;
98 const step = 11;
99 const ys = Array.from({length: count}, (_, i) => (i - (count - 1) / 2) * step);
100
101 function circleHalfWidth(y, r){
102 const v = r*r - y*y;
103 return v > 0 ? Math.sqrt(v) : 0;
104 }
105
106 // Left stem + right bowl (b)
107 function shapeB(y){
108 const stemLeft = -92, stemRight = -18;
109 const stemW = stemRight - stemLeft;
110
111 const cx = 28, cy = 0, r = 78;
112 const hw = circleHalfWidth(y - cy, r);
113 const bowlLeft = cx - hw;
114 const bowlRight = cx + hw;
115
116 const left = Math.min(stemLeft, bowlLeft);
117 const right = Math.max(stemRight, bowlRight);
118
119 // carve a slight waist / notch where stem meets bowl
120 let notch = 0;
121 if (Math.abs(y) < 18) notch = 10 - Math.abs(y) * 0.35;
122
123 return {
124 x: (left + right) / 2 + notch * 0.15,
125 w: Math.max(stemW, right - left - notch)
126 };
127 }
128
129 // Descender stem + upper bowl (p)
130 function shapeP(y){
131 const stemLeft = 8, stemRight = 82;
132 const stemW = stemRight - stemLeft;
133
134 const cx = -18, cy = -18, r = 82;
135 const hw = circleHalfWidth(y - cy, r);
136 const bowlLeft = cx - hw;
137 const bowlRight = cx + hw;
138
139 let left = Math.min(stemLeft, bowlLeft);
140 let right = Math.max(stemRight, bowlRight);
141
142 // lower descender extension
143 if (y > 34){
144 left = Math.min(left, -92);
145 right = Math.max(right, 18);
146 }
147
148 // slight narrowing near join
149 let notch = 0;
150 if (y > -6 && y < 28) notch = 12 - Math.abs(y - 11) * 0.45;
151
152 return {
153 x: (left + right) / 2 - notch * 0.12,
154 w: Math.max(stemW, right - left - notch)
155 };
156 }
157
158 ys.forEach((y, i) => {
159 const a = shapeB(y);
160 const b = shapeP(y);
161
162 const line = document.createElement('div');
163 line.className = 'line';
164
165 // small deterministic irregularity to mimic the uneven line lengths/offsets
166 const jitterX0 = Math.sin(i * 1.73) * 4;
167 const jitterX1 = Math.cos(i * 1.21) * 4;
168 const jitterW0 = Math.sin(i * 0.91) * 10;
169 const jitterW1 = Math.cos(i * 0.77) * 10;
170
171 line.style.setProperty('--y', y.toFixed(2));
172 line.style.setProperty('--x0', (a.x + jitterX0).toFixed(2));
173 line.style.setProperty('--w0', Math.max(18, a.w + jitterW0).toFixed(2));
174 line.style.setProperty('--x1', (b.x + jitterX1).toFixed(2));
175 line.style.setProperty('--w1', Math.max(18, b.w + jitterW1).toFixed(2));
176
177 // stagger slightly so the morph feels organic but still cohesive
178 line.style.animationDelay = `${(-i * 0.035).toFixed(3)}s`;
179 line.style.opacity = (0.72 + (1 - Math.abs(y)/150) * 0.28).toFixed(2);
180
181 shape.appendChild(line);
182 });
183})();
184</script>
185</body>
186</html>