← back to Only CSS: 3D Scan 2compare with ground truth →
model output
GPT-5.4
Only CSS: 3D Scan 2
A 0.83T 0.43
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>Wireframe Cube Dice Animation</title>
7<style>
8 :root{
9 --bg1:#0a0d16;
10 --bg2:#132235;
11 --wire:rgba(210,230,255,.18);
12 --wire2:rgba(210,230,255,.10);
13 --green:#59f07d;
14 --purple:#a86bff;
15 --blue:#5f6dff;
16 }
17
18 html,body{
19 margin:0;
20 height:100%;
21 overflow:hidden;
22 background:
23 radial-gradient(circle at 50% 58%, #17304a 0%, #0d1624 34%, #090c15 72%, #070910 100%);
24 font-family:system-ui,sans-serif;
25 }
26
27 .scene{
28 width:100vw;
29 height:100vh;
30 display:grid;
31 place-items:center;
32 }
33
34 svg{
35 width:min(78vmin,760px);
36 height:min(78vmin,760px);
37 overflow:visible;
38 filter: drop-shadow(0 0 10px rgba(120,180,255,.05));
39 }
40
41 .cube line,.cube polyline,.cube rect{
42 stroke:var(--wire);
43 stroke-width:1.1;
44 fill:none;
45 vector-effect:non-scaling-stroke;
46 }
47
48 .cube .inner{
49 stroke:var(--wire2);
50 }
51
52 .pip{
53 fill:none;
54 stroke-width:2.2;
55 vector-effect:non-scaling-stroke;
56 opacity:.95;
57 mix-blend-mode:screen;
58 }
59
60 .pip.green{ stroke:var(--green); filter:drop-shadow(0 0 3px rgba(89,240,125,.18)); }
61 .pip.purple{ stroke:var(--purple); filter:drop-shadow(0 0 3px rgba(168,107,255,.18)); }
62 .pip.blue{ stroke:var(--blue); opacity:.8; }
63
64 .ghost{
65 fill:none;
66 stroke-width:1.1;
67 opacity:.55;
68 vector-effect:non-scaling-stroke;
69 }
70
71 .ghost.green{ stroke:rgba(89,240,125,.45); }
72 .ghost.purple{ stroke:rgba(168,107,255,.42); }
73 .ghost.blue{ stroke:rgba(95,109,255,.35); }
74
75 .tiny{
76 fill:none;
77 stroke:rgba(190,220,255,.35);
78 stroke-width:.9;
79 vector-effect:non-scaling-stroke;
80 }
81</style>
82</head>
83<body>
84<div class="scene">
85 <svg viewBox="0 0 800 800" aria-label="Animated wireframe cube with moving neon rings">
86 <g id="cube" class="cube"></g>
87 <g id="rings"></g>
88 <g id="tiny"></g>
89 </svg>
90</div>
91
92<script>
93(() => {
94 const svgNS = "http://www.w3.org/2000/svg";
95 const cube = document.getElementById("cube");
96 const ringsLayer = document.getElementById("rings");
97 const tinyLayer = document.getElementById("tiny");
98
99 const W = 800, H = 800;
100 const cx = 400, cy = 400;
101
102 // Cube geometry tuned to match the reference perspective.
103 const front = { x: 275, y: 205, w: 250, h: 390 };
104 const dx = 120, dy = 48;
105 const depthSteps = 18;
106
107 function el(name, attrs={}, parent){
108 const n = document.createElementNS(svgNS, name);
109 for(const k in attrs) n.setAttribute(k, attrs[k]);
110 if(parent) parent.appendChild(n);
111 return n;
112 }
113
114 function lerp(a,b,t){ return a + (b-a)*t; }
115 function easeInOut(t){ return 0.5 - 0.5*Math.cos(Math.PI*t); }
116
117 function project(u,v,z){
118 // u,v in [0,1], z in [0,1] from front to back
119 return {
120 x: front.x + u*front.w + z*dx,
121 y: front.y + v*front.h - z*dy
122 };
123 }
124
125 // Draw stacked wireframe cube slices.
126 for(let i=0;i<depthSteps;i++){
127 const z = i/(depthSteps-1);
128 const p = project(0,0,z);
129 el("rect", {
130 x:p.x, y:p.y, width:front.w, height:front.h, rx:10, ry:10,
131 class:i < depthSteps-1 ? "inner" : ""
132 }, cube);
133 }
134
135 // Connect corners.
136 const corners = [
137 [[0,0,0],[0,0,1]], [[1,0,0],[1,0,1]],
138 [[0,1,0],[0,1,1]], [[1,1,0],[1,1,1]]
139 ];
140 corners.forEach(c=>{
141 const a = project(...c[0]), b = project(...c[1]);
142 el("line",{x1:a.x,y1:a.y,x2:b.x,y2:b.y},cube);
143 });
144
145 // Dice-like positions on front face.
146 const slots = {
147 tl:[0.22,0.22],
148 tc:[0.50,0.22],
149 tr:[0.78,0.22],
150 ml:[0.34,0.50],
151 mc:[0.50,0.50],
152 mr:[0.66,0.50],
153 bl:[0.22,0.78],
154 bc:[0.50,0.78],
155 br:[0.78,0.78]
156 };
157
158 // Ring groups approximate the frame sequence:
159 // green clusters drift into a 5-like arrangement while purple recedes.
160 const rings = [
161 {from:"tl", to:"tl", z0:.10, z1:.34, color:"green", r:42, copies:4, phase:0.00},
162 {from:"tc", to:"tr", z0:.28, z1:.18, color:"green", r:48, copies:4, phase:0.08},
163 {from:"mc", to:"mc", z0:.22, z1:.22, color:"green", r:36, copies:4, phase:0.16},
164 {from:"bc", to:"br", z0:.26, z1:.18, color:"green", r:46, copies:4, phase:0.24},
165 {from:"bl", to:"bl", z0:.12, z1:.30, color:"green", r:42, copies:4, phase:0.32},
166
167 {from:"tr", to:"ml", z0:.18, z1:.08, color:"purple", r:40, copies:4, phase:0.50},
168 {from:"mr", to:"ml", z0:.18, z1:.10, color:"purple", r:40, copies:4, phase:0.62},
169 {from:"tc", to:"ml", z0:.12, z1:.12, color:"purple", r:34, copies:3, phase:0.74},
170 {from:"bc", to:"ml", z0:.12, z1:.12, color:"purple", r:34, copies:3, phase:0.86},
171
172 {from:"mr", to:"mr", z0:.42, z1:.42, color:"blue", r:28, copies:1, phase:0.18},
173 {from:"tc", to:"tc", z0:.46, z1:.46, color:"blue", r:26, copies:1, phase:0.58}
174 ];
175
176 const ringNodes = rings.map(r=>{
177 const g = el("g",{},ringsLayer);
178 const circles = [];
179 for(let i=0;i<r.copies;i++){
180 circles.push(el("circle",{class:`pip ${r.color}`, r:r.r},g));
181 }
182 return {cfg:r, circles};
183 });
184
185 // Tiny faint circles seen inside some positions.
186 const tinySpecs = [
187 {slot:"tl", z:.18}, {slot:"tr", z:.42}, {slot:"tc", z:.34},
188 {slot:"ml", z:.10}, {slot:"mc", z:.22}, {slot:"mr", z:.46},
189 {slot:"bl", z:.14}, {slot:"bc", z:.28}, {slot:"br", z:.18}
190 ];
191 const tinyNodes = tinySpecs.map(s => el("circle",{class:"tiny", r:8}, tinyLayer));
192
193 const duration = 3200; // smooth loop close to the sampled sequence
194
195 function slotPos(name){ return slots[name]; }
196
197 function update(now){
198 const t = (now % duration) / duration;
199 const e = easeInOut(t);
200
201 ringNodes.forEach(({cfg,circles})=>{
202 const [u0,v0] = slotPos(cfg.from);
203 const [u1,v1] = slotPos(cfg.to);
204
205 const u = lerp(u0,u1,e);
206 const v = lerp(v0,v1,e);
207 const z = lerp(cfg.z0,cfg.z1,e);
208
209 const base = project(u,v,z);
210
211 circles.forEach((c,i)=>{
212 const spread = (i - (circles.length-1)/2);
213 const localZ = Math.max(0, Math.min(1, z + spread*0.035));
214 const p = project(u,v,localZ);
215
216 // Slight horizontal ellipse feel from perspective by scaling x via rx illusion:
217 c.setAttribute("cx", p.x + spread*2.5);
218 c.setAttribute("cy", p.y);
219 c.setAttribute("r", cfg.r + Math.sin((t + cfg.phase + i*0.03)*Math.PI*2)*1.2);
220 c.setAttribute("opacity", cfg.color === "blue" ? 0.72 : 0.92 - Math.abs(spread)*0.08);
221 });
222 });
223
224 tinyNodes.forEach((n,i)=>{
225 const s = tinySpecs[i];
226 const [u,v] = slotPos(s.slot);
227 const p = project(u,v,s.z);
228 const drift = Math.sin(t*Math.PI*2 + i*0.8)*2;
229 n.setAttribute("cx", p.x + drift);
230 n.setAttribute("cy", p.y + Math.cos(t*Math.PI*2 + i)*1.5);
231 n.setAttribute("opacity", 0.18 + 0.18*Math.sin(t*Math.PI*2 + i*0.7 + 1));
232 });
233
234 requestAnimationFrame(update);
235 }
236
237 requestAnimationFrame(update);
238})();
239</script>
240</body>
241</html>