animation2code benchmark
For best compatibility, please view this dashboard in a Chrome browser.
← back to Only CSS: Star Warp Display

model output

GPT-5.4

Only CSS: Star Warp Display

A 0.85T 0.29
compare with ground truth →
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>Radial Streak Panel Animation</title>
7<style>
8  :root{
9    --bg:#172a43;
10    --panel:#0d0d0d;
11    --panel-edge:#171717;
12    --shine:rgba(255,255,255,.045);
13  }
14
15  *{box-sizing:border-box}
16  html,body{
17    margin:0;
18    height:100%;
19    background:var(--bg);
20    overflow:hidden;
21    font-family:system-ui,sans-serif;
22  }
23
24  .stage{
25    width:100vw;
26    height:100vh;
27    display:grid;
28    place-items:center;
29    position:relative;
30  }
31
32  .phone-wrap{
33    position:relative;
34    width:min(29vw,300px);
35    aspect-ratio: 300 / 500;
36    filter: drop-shadow(0 18px 14px rgba(0,0,0,.22));
37  }
38
39  .phone{
40    position:relative;
41    width:100%;
42    height:100%;
43    background:
44      linear-gradient(121deg,
45        rgba(255,255,255,.04) 0%,
46        rgba(255,255,255,.04) 38%,
47        rgba(255,255,255,0) 38.5%,
48        rgba(255,255,255,0) 100%),
49      linear-gradient(180deg,#111 0%, #0d0d0d 100%);
50    border:2px solid var(--panel-edge);
51    overflow:hidden;
52  }
53
54  .phone::after{
55    content:"";
56    position:absolute;
57    left:-10%;
58    right:-10%;
59    bottom:-28px;
60    height:34px;
61    background:radial-gradient(ellipse at center, rgba(0,0,0,.28) 0%, rgba(0,0,0,.16) 35%, rgba(0,0,0,0) 72%);
62    pointer-events:none;
63  }
64
65  #streaks{
66    position:absolute;
67    inset:0;
68    width:100%;
69    height:100%;
70    display:block;
71  }
72
73  @media (max-width:700px){
74    .phone-wrap{ width:min(42vw,300px); }
75  }
76</style>
77</head>
78<body>
79<div class="stage">
80  <div class="phone-wrap">
81    <div class="phone">
82      <canvas id="streaks"></canvas>
83    </div>
84  </div>
85</div>
86
87<script>
88(() => {
89  const canvas = document.getElementById('streaks');
90  const ctx = canvas.getContext('2d');
91
92  let w = 0, h = 0, dpr = Math.min(devicePixelRatio || 1, 2);
93  let particles = [];
94  const COUNT = 34;
95  const LOOP = 2600; // ms, close to the sampled sequence cadence
96
97  function resize() {
98    const rect = canvas.getBoundingClientRect();
99    w = Math.round(rect.width);
100    h = Math.round(rect.height);
101    canvas.width = Math.round(w * dpr);
102    canvas.height = Math.round(h * dpr);
103    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
104    initParticles();
105  }
106
107  function rand(a, b){ return a + Math.random() * (b - a); }
108  function pick(a){ return a[(Math.random() * a.length) | 0]; }
109
110  function initParticles() {
111    particles = [];
112    for (let i = 0; i < COUNT; i++) particles.push(makeParticle(i));
113  }
114
115  function makeParticle(i){
116    const angle = rand(0, Math.PI * 2);
117    const speed = rand(0.18, 1.0);
118    const len = rand(2, 18);
119    const thick = rand(0.6, 2.8);
120    const bright = rand(180, 255);
121    const delay = rand(0, LOOP);
122    const life = rand(900, 1700);
123    const centerBiasX = rand(-0.08, 0.08) * w;
124    const centerBiasY = rand(-0.08, 0.08) * h;
125
126    return {
127      angle,
128      speed,
129      len,
130      thick,
131      bright,
132      delay,
133      life,
134      centerBiasX,
135      centerBiasY,
136      maxR: Math.hypot(w, h) * rand(0.42, 0.72),
137      twinkle: rand(0.7, 1.5),
138      cluster: Math.random() < 0.65
139    };
140  }
141
142  function easeOutCubic(t){ return 1 - Math.pow(1 - t, 3); }
143  function easeInQuad(t){ return t * t; }
144
145  function draw(now){
146    ctx.clearRect(0,0,w,h);
147
148    // subtle dark vignette inside panel
149    const vg = ctx.createRadialGradient(w*0.5, h*0.5, 0, w*0.5, h*0.5, Math.max(w,h)*0.72);
150    vg.addColorStop(0, 'rgba(0,0,0,0)');
151    vg.addColorStop(1, 'rgba(0,0,0,0.18)');
152    ctx.fillStyle = vg;
153    ctx.fillRect(0,0,w,h);
154
155    const cx = w * 0.5;
156    const cy = h * 0.5;
157
158    for (const p of particles){
159      let t = (now - p.delay) % LOOP;
160      if (t < 0) t += LOOP;
161
162      let local = t / p.life;
163      if (local > 1) local = 1;
164
165      // burst mostly visible in first ~65% of each particle life, then fades
166      const travel = easeOutCubic(local);
167      const fade = local < 0.72 ? 1 : 1 - easeInQuad((local - 0.72) / 0.28);
168      if (fade <= 0.01) continue;
169
170      const startR = p.cluster ? rand(0, 10) : rand(8, 26);
171      const r = startR + p.maxR * travel * p.speed;
172
173      const x = cx + p.centerBiasX + Math.cos(p.angle) * r;
174      const y = cy + p.centerBiasY + Math.sin(p.angle) * r;
175
176      const dx = Math.cos(p.angle);
177      const dy = Math.sin(p.angle);
178
179      // streaks lengthen as they move outward
180      const L = p.len * (0.45 + travel * 1.9);
181      const alpha = fade * (0.18 + 0.82 * Math.sin((t / LOOP) * Math.PI * 2 * p.twinkle + iHash(p.angle)) ** 2);
182
183      ctx.strokeStyle = `rgba(${p.bright},${p.bright},${p.bright},${alpha.toFixed(3)})`;
184      ctx.lineWidth = p.thick * (0.8 + travel * 0.55);
185      ctx.lineCap = 'butt';
186
187      ctx.beginPath();
188      ctx.moveTo(x - dx * L * 0.5, y - dy * L * 0.5);
189      ctx.lineTo(x + dx * L * 0.5, y + dy * L * 0.5);
190      ctx.stroke();
191
192      // occasional tiny spark dots near center
193      if (travel < 0.35 && p.thick < 1.4) {
194        ctx.fillStyle = `rgba(255,255,255,${(alpha * 0.55).toFixed(3)})`;
195        ctx.fillRect(x + rand(-1,1), y + rand(-1,1), 1.2, 1.2);
196      }
197    }
198
199    requestAnimationFrame(draw);
200  }
201
202  function iHash(v){
203    return (Math.sin(v * 91.73) * 43758.5453) % (Math.PI * 2);
204  }
205
206  window.addEventListener('resize', resize, {passive:true});
207  resize();
208
209  // deterministic-ish reseeding to keep the composition changing like the frames
210  setInterval(() => {
211    for (let i = 0; i < particles.length; i++) {
212      if (Math.random() < 0.35) particles[i] = makeParticle(i);
213    }
214  }, 900);
215
216  requestAnimationFrame(draw);
217})();
218</script>
219</body>
220</html>