← back to Only CSS: Codevember #6 Money Stormcompare with ground truth →
model output
GPT-5.4
Only CSS: Codevember #6 Money Storm
A 0.56T 0.30
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>Falling Money</title>
7<style>
8 :root{
9 --bg:#000;
10 --paper:#f4efe3;
11 --paper2:#ece4d4;
12 --paper3:#e6ddcc;
13 --ink:#5f6f57;
14 }
15 html,body{
16 margin:0;
17 width:100%;
18 height:100%;
19 overflow:hidden;
20 background:var(--bg);
21 }
22 body{
23 perspective: 900px;
24 perspective-origin: 50% 50%;
25 font-family: Arial, Helvetica, sans-serif;
26 }
27 #scene{
28 position:fixed;
29 inset:0;
30 overflow:hidden;
31 transform-style:preserve-3d;
32 background:#000;
33 }
34 .bill{
35 position:absolute;
36 left:0; top:0;
37 width:120px;
38 height:56px;
39 transform-style:preserve-3d;
40 will-change:transform;
41 pointer-events:none;
42 }
43 .bill .strip{
44 position:absolute;
45 left:0;
46 width:100%;
47 height:25%;
48 transform-style:preserve-3d;
49 backface-visibility:hidden;
50 background:
51 linear-gradient(180deg, rgba(255,255,255,.18), rgba(0,0,0,.03)),
52 var(--paper);
53 border-radius:1px;
54 box-sizing:border-box;
55 overflow:hidden;
56 }
57 .bill .strip:nth-child(1){ top:0; background:
58 linear-gradient(180deg, rgba(255,255,255,.18), rgba(0,0,0,.03)),
59 var(--paper);}
60 .bill .strip:nth-child(2){ top:25%; background:
61 linear-gradient(180deg, rgba(255,255,255,.12), rgba(0,0,0,.04)),
62 var(--paper2);}
63 .bill .strip:nth-child(3){ top:50%; background:
64 linear-gradient(180deg, rgba(255,255,255,.10), rgba(0,0,0,.05)),
65 var(--paper);}
66 .bill .strip:nth-child(4){ top:75%; background:
67 linear-gradient(180deg, rgba(255,255,255,.14), rgba(0,0,0,.03)),
68 var(--paper3);}
69 .face{
70 position:absolute;
71 inset:0;
72 display:flex;
73 align-items:center;
74 justify-content:center;
75 color:var(--ink);
76 font-weight:700;
77 font-size:22px;
78 letter-spacing:1px;
79 opacity:.95;
80 mix-blend-mode:normal;
81 text-shadow:0 0 .2px rgba(0,0,0,.2);
82 }
83 .face::before,
84 .face::after{
85 content:"¥10,000";
86 position:absolute;
87 font-size:12px;
88 opacity:.9;
89 }
90 .face::before{ left:8px; top:5px; }
91 .face::after{ right:8px; bottom:5px; transform:rotate(180deg); }
92</style>
93</head>
94<body>
95<div id="scene"></div>
96
97<script>
98(() => {
99 const scene = document.getElementById('scene');
100 const W = () => innerWidth;
101 const H = () => innerHeight;
102
103 const COUNT = 34;
104 const bills = [];
105 const rand = (a,b)=>a + Math.random()*(b-a);
106
107 function makeBill(scale){
108 const bill = document.createElement('div');
109 bill.className = 'bill';
110 bill.style.width = `${120*scale}px`;
111 bill.style.height = `${56*scale}px`;
112
113 for(let i=0;i<4;i++){
114 const s = document.createElement('div');
115 s.className = 'strip';
116 const face = document.createElement('div');
117 face.className = 'face';
118 if(i===1) face.textContent = '¥10,000';
119 s.appendChild(face);
120 bill.appendChild(s);
121 }
122 scene.appendChild(bill);
123 return bill;
124 }
125
126 function resetBill(b, initial=false){
127 const depth = rand(-500, 500);
128 const scale = rand(0.55, 2.8) * (depth > 180 ? 1.25 : 1);
129 if(!b.el){
130 b.el = makeBill(scale);
131 }else{
132 b.el.style.width = `${120*scale}px`;
133 b.el.style.height = `${56*scale}px`;
134 }
135
136 b.scale = scale;
137 b.x = rand(W()*0.52, W()*0.78);
138 b.y = initial ? rand(-H()*0.2, H()*0.9) : rand(-H()*0.35, -40);
139 b.z = depth;
140
141 b.vx = rand(-18, 18);
142 b.vy = rand(70, 145);
143 b.vz = rand(-18, 18);
144
145 b.ry = rand(0, Math.PI*2);
146 b.rx = rand(-0.8, 0.8);
147 b.rz = rand(-1.2, 1.2);
148
149 b.spinY = rand(-1.8, 1.8);
150 b.spinZ = rand(-1.2, 1.2);
151 b.flutter = rand(4.5, 8.5);
152 b.flutterSpeed = rand(3.2, 6.8);
153 b.phase = rand(0, Math.PI*2);
154 b.drift = rand(10, 40);
155 b.driftSpeed = rand(.4, 1.2);
156 b.tilt = rand(.15, .55);
157
158 b.segs = [...b.el.children];
159 }
160
161 for(let i=0;i<COUNT;i++){
162 const b = {};
163 resetBill(b, true);
164 bills.push(b);
165 }
166
167 let last = performance.now();
168
169 function frame(now){
170 const dt = Math.min(0.033, (now - last) / 1000);
171 last = now;
172 const t = now / 1000;
173
174 for(const b of bills){
175 b.y += b.vy * dt;
176 b.x += (b.vx + Math.sin(t*b.driftSpeed + b.phase) * b.drift) * dt;
177 b.z += b.vz * dt;
178
179 b.ry += b.spinY * dt;
180 b.rz += b.spinZ * dt;
181
182 const flap = Math.sin(t * b.flutterSpeed + b.phase) * b.flutter;
183 const flap2 = Math.sin(t * b.flutterSpeed + b.phase + .8) * b.flutter * .72;
184 const flap3 = Math.sin(t * b.flutterSpeed + b.phase + 1.6) * b.flutter * .52;
185
186 b.rx = Math.sin(t * 1.7 + b.phase) * b.tilt;
187
188 const segAngles = [flap, flap2, -flap2, -flap3];
189 for(let i=0;i<4;i++){
190 const s = b.segs[i];
191 const origin = i < 2 ? '50% 100%' : '50% 0%';
192 s.style.transformOrigin = origin;
193 s.style.transform = `perspective(300px) rotateX(${segAngles[i]}deg)`;
194 }
195
196 const perspectiveScale = 1 + (b.z / 1200);
197 b.el.style.transform =
198 `translate3d(${b.x}px, ${b.y}px, ${b.z}px)
199 rotateX(${b.rx}rad)
200 rotateY(${b.ry}rad)
201 rotateZ(${b.rz}rad)
202 scale(${perspectiveScale})`;
203
204 if (b.y > H() + 180 || b.x < -260 || b.x > W() + 260 || b.z < -900 || b.z > 900) {
205 resetBill(b, false);
206 }
207 }
208
209 requestAnimationFrame(frame);
210 }
211
212 addEventListener('resize', () => {
213 for (const b of bills) {
214 if (b.x > W()) b.x = rand(W()*0.45, W()*0.8);
215 }
216 });
217
218 requestAnimationFrame(frame);
219})();
220</script>
221</body>
222</html>