← back to Motion Table - Solid Rotationcompare with ground truth →
model output
GPT-5.4
Motion Table - Solid Rotation
A 0.75T 0.27
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>Geometric Orbit Loop</title>
7<style>
8 :root{
9 --bg:#4a4648;
10 --line:rgba(255,255,255,.72);
11 --line-soft:rgba(255,255,255,.38);
12 }
13 html,body{
14 margin:0;
15 height:100%;
16 background:var(--bg);
17 overflow:hidden;
18 }
19 body{
20 display:grid;
21 place-items:center;
22 }
23 canvas{
24 width:100vw;
25 height:100vh;
26 display:block;
27 }
28</style>
29</head>
30<body>
31<canvas id="c"></canvas>
32<script>
33const canvas = document.getElementById('c');
34const ctx = canvas.getContext('2d');
35
36function resize(){
37 const dpr = Math.min(devicePixelRatio || 1, 2);
38 canvas.width = innerWidth * dpr;
39 canvas.height = innerHeight * dpr;
40 canvas.style.width = innerWidth + 'px';
41 canvas.style.height = innerHeight + 'px';
42 ctx.setTransform(dpr,0,0,dpr,0,0);
43}
44addEventListener('resize', resize);
45resize();
46
47const TAU = Math.PI * 2;
48const cycle = 6.4; // smooth looping duration
49
50function easeInOut(t){ return 0.5 - 0.5 * Math.cos(Math.PI * t); }
51function mix(a,b,t){ return a + (b-a)*t; }
52function clamp(v,a,b){ return Math.max(a, Math.min(b, v)); }
53function seg(t,a,b){
54 return clamp((t-a)/(b-a),0,1);
55}
56
57function drawEllipseOrbit(cx, cy, r, rot, sx, sy, alpha, dashed=false, lw=1){
58 ctx.save();
59 ctx.translate(cx, cy);
60 ctx.rotate(rot);
61 ctx.scale(sx, sy);
62 ctx.beginPath();
63 ctx.arc(0, 0, r, 0, TAU);
64 ctx.lineWidth = lw;
65 ctx.strokeStyle = dashed ? `rgba(255,255,255,${alpha*0.55})` : `rgba(255,255,255,${alpha})`;
66 ctx.setLineDash(dashed ? [2.2, 6.2] : []);
67 ctx.stroke();
68 ctx.restore();
69}
70
71function drawPolygon(cx, cy, radius, sides, rot, alpha, lw=1){
72 ctx.save();
73 ctx.translate(cx, cy);
74 ctx.rotate(rot);
75 ctx.beginPath();
76 for(let i=0;i<sides;i++){
77 const a = -Math.PI/2 + i*TAU/sides;
78 const x = Math.cos(a)*radius;
79 const y = Math.sin(a)*radius;
80 if(i===0) ctx.moveTo(x,y); else ctx.lineTo(x,y);
81 }
82 ctx.closePath();
83 ctx.lineWidth = lw;
84 ctx.strokeStyle = `rgba(255,255,255,${alpha})`;
85 ctx.stroke();
86 ctx.restore();
87}
88
89function render(ms){
90 const w = innerWidth, h = innerHeight;
91 const t = (ms/1000 % cycle) / cycle;
92
93 ctx.clearRect(0,0,w,h);
94
95 const cx = w * 0.5;
96 const cy = h * 0.5;
97 const baseR = Math.min(w,h) * 0.125;
98
99 // overall scale breathes slightly like the reference
100 const scale =
101 1
102 - 0.12 * easeInOut(seg(t, .18, .34))
103 + 0.08 * easeInOut(seg(t, .34, .52))
104 - 0.06 * easeInOut(seg(t, .72, .90));
105
106 const R = baseR * scale;
107
108 // outer ring always present
109 ctx.beginPath();
110 ctx.arc(cx, cy, R, 0, TAU);
111 ctx.lineWidth = 1.15;
112 ctx.strokeStyle = 'rgba(255,255,255,.82)';
113 ctx.stroke();
114
115 // phase envelopes
116 const p1 = easeInOut(seg(t, .14, .34)); // first emergence
117 const p2 = easeInOut(seg(t, .30, .58)); // dense ring
118 const p3 = easeInOut(seg(t, .52, .82)); // star / inner detail
119 const p4 = easeInOut(seg(t, .76, 1.00)); // fade back
120
121 // 6 main orbiting ellipses
122 const n = 6;
123 for(let i=0;i<n;i++){
124 const a = i * TAU / n;
125 const rot =
126 a
127 + TAU * (0.18*t)
128 + 0.22 * Math.sin(TAU*(t*2 + i*0.13));
129
130 const sx = mix(1, 0.62, p1);
131 const sy = mix(1, 0.62, p1);
132
133 let alpha = 0;
134 alpha += 0.34 * p1;
135 alpha += 0.22 * p2;
136 alpha *= (1 - 0.25*p4);
137
138 drawEllipseOrbit(cx, cy, R, rot, sx, sy, alpha, false, 1);
139
140 // dotted inner guides appear later
141 const dAlpha = 0.34 * p3 * (1 - 0.35*p4);
142 if(dAlpha > 0.002){
143 const dsx = mix(0.62, 0.42, p3);
144 const dsy = mix(0.62, 0.42, p3);
145 drawEllipseOrbit(cx, cy, R*0.98, -rot*1.08 + 0.35, dsx, dsy, dAlpha, true, 1);
146 }
147 }
148
149 // extra dense outer traces during middle / late frames
150 const dense = Math.round(mix(0, 6, p2 + p4*0.7));
151 for(let i=0;i<dense;i++){
152 const a = i * TAU / Math.max(1,dense) + TAU*t*0.11;
153 drawEllipseOrbit(
154 cx, cy, R,
155 a,
156 0.88 + 0.18*Math.sin(i*1.7 + t*TAU),
157 0.88 + 0.18*Math.cos(i*1.3 + t*TAU),
158 0.18 + 0.12*p2,
159 false, 1
160 );
161 }
162
163 // central polygon/star transitions
164 const starA = 0.42 * p3 * (1 - 0.55*p4);
165 if(starA > 0.002){
166 const sides = t < 0.62 ? 6 : 7;
167 drawPolygon(cx, cy, R * mix(0.18, 0.34, p3), sides, TAU*t*0.35, starA, 1);
168 }
169
170 // subtle inner ticks / spark points
171 const tickA = 0.42 * p3 * (1 - 0.4*p4);
172 if(tickA > 0.002){
173 ctx.save();
174 ctx.translate(cx, cy);
175 ctx.rotate(TAU*t*0.12);
176 ctx.strokeStyle = `rgba(255,255,255,${tickA})`;
177 ctx.lineWidth = 1;
178 const m = 12;
179 for(let i=0;i<m;i++){
180 const a = i * TAU / m;
181 const r0 = R * 0.28;
182 const r1 = R * 0.33;
183 ctx.beginPath();
184 ctx.moveTo(Math.cos(a)*r0, Math.sin(a)*r0);
185 ctx.lineTo(Math.cos(a)*r1, Math.sin(a)*r1);
186 ctx.stroke();
187 }
188 ctx.restore();
189 }
190
191 requestAnimationFrame(render);
192}
193requestAnimationFrame(render);
194</script>
195</body>
196</html>