← back to Only CSS: Codevember #6 Money Stormcompare with ground truth →
model output
Claude Sonnet 4.6
Only CSS: Codevember #6 Money Storm
A 0.77T 0.31
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>Money Rain Animation</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9
10 body {
11 background: #000;
12 width: 100vw;
13 height: 100vh;
14 overflow: hidden;
15 }
16
17 canvas {
18 display: block;
19 }
20</style>
21</head>
22<body>
23<canvas id="c"></canvas>
24<script>
25const canvas = document.getElementById('c');
26const ctx = canvas.getContext('2d');
27canvas.width = window.innerWidth;
28canvas.height = window.innerHeight;
29
30const W = canvas.width;
31const H = canvas.height;
32
33// Draw a bill on an offscreen canvas
34function createBillCanvas(w, h) {
35 const bc = document.createElement('canvas');
36 bc.width = w;
37 bc.height = h;
38 const bx = bc.getContext('2d');
39
40 // Bill background
41 bx.fillStyle = '#e8e8d8';
42 bx.fillRect(0, 0, w, h);
43
44 // Shadow/fold effect
45 bx.fillStyle = '#d0d0c0';
46 bx.fillRect(0, h * 0.45, w, h * 0.1);
47
48 // Border
49 bx.strokeStyle = '#c0c0a0';
50 bx.lineWidth = 1;
51 bx.strokeRect(2, 2, w-4, h-4);
52
53 // Text
54 bx.fillStyle = '#1a4a1a';
55 bx.font = `bold ${Math.floor(h * 0.35)}px Arial`;
56 bx.textAlign = 'center';
57 bx.textBaseline = 'middle';
58 bx.fillText('¥10,000', w/2, h/2);
59
60 // Small text
61 bx.font = `${Math.floor(h * 0.15)}px Arial`;
62 bx.fillText('¥ 10,000', w/2, h * 0.8);
63
64 return bc;
65}
66
67const billW = 120;
68const billH = 60;
69const billCanvas = createBillCanvas(billW, billH);
70
71class Bill {
72 constructor(delay) {
73 this.delay = delay;
74 this.reset(true);
75 }
76
77 reset(initial) {
78 // Start from upper-right area (matching the video)
79 this.x = W * 0.4 + Math.random() * W * 0.4;
80 this.y = initial ? -Math.random() * H * 0.5 - 50 : -80;
81
82 // Velocity - bills fall and spread
83 this.vx = (Math.random() - 0.7) * 3;
84 this.vy = 1.5 + Math.random() * 2.5;
85
86 // Rotation
87 this.angle = Math.random() * Math.PI * 2;
88 this.rotX = (Math.random() - 0.5) * 0.08; // 3D-like rotation speed X
89 this.rotZ = (Math.random() - 0.5) * 0.05; // Z rotation speed
90
91 // Scale for perspective effect
92 this.scale = 0.3 + Math.random() * 1.2;
93 this.scaleX = 1; // for 3D flip effect
94 this.scaleXDir = (Math.random() > 0.5 ? 1 : -1) * 0.02;
95
96 // Wobble
97 this.wobble = Math.random() * Math.PI * 2;
98 this.wobbleSpeed = 0.05 + Math.random() * 0.05;
99
100 this.alive = true;
101 this.born = false;
102 this.birthTime = this.delay;
103 }
104
105 update(t) {
106 if (t < this.birthTime) return;
107 this.born = true;
108
109 this.x += this.vx;
110 this.y += this.vy;
111 this.vy += 0.02; // gravity
112 this.vx += (Math.random() - 0.5) * 0.1;
113
114 this.angle += this.rotZ;
115 this.wobble += this.wobbleSpeed;
116
117 // 3D flip effect
118 this.scaleX += this.scaleXDir;
119 if (Math.abs(this.scaleX) > 1) this.scaleXDir *= -1;
120
121 if (this.y > H + 100) {
122 this.alive = false;
123 }
124 }
125
126 draw(ctx) {
127 if (!this.born) return;
128
129 ctx.save();
130 ctx.translate(this.x, this.y);
131 ctx.rotate(this.angle);
132 ctx.scale(this.scale * this.scaleX, this.scale * (0.7 + 0.3 * Math.abs(Math.sin(this.wobble))));
133
134 // Draw bill with fold effect
135 ctx.drawImage(billCanvas, -billW/2, -billH/2, billW, billH);
136
137 // Add shadow/3D fold
138 if (this.scaleX > 0.3) {
139 ctx.fillStyle = 'rgba(0,0,0,0.2)';
140 ctx.fillRect(-billW/2, -billH/2 + billH * 0.4, billW, billH * 0.15);
141 }
142
143 ctx.restore();
144 }
145}
146
147// Create bills with staggered delays
148const bills = [];
149const totalBills = 35;
150
151for (let i = 0; i < totalBills; i++) {
152 bills.push(new Bill(i * 80)); // stagger by 80ms
153}
154
155let startTime = null;
156let lastSpawn = 0;
157
158function animate(timestamp) {
159 if (!startTime) startTime = timestamp;
160 const elapsed = timestamp - startTime;
161
162 ctx.clearRect(0, 0, W, H);
163 ctx.fillStyle = '#000';
164 ctx.fillRect(0, 0, W, H);
165
166 // Respawn dead bills
167 for (let b of bills) {
168 b.update(elapsed);
169 if (!b.alive) {
170 b.delay = elapsed + Math.random() * 500;
171 b.x = W * 0.3 + Math.random() * W * 0.5;
172 b.y = -80;
173 b.vx = (Math.random() - 0.6) * 3;
174 b.vy = 1.5 + Math.random() * 2;
175 b.angle = Math.random() * Math.PI * 2;
176 b.scale = 0.3 + Math.random() * 1.2;
177 b.scaleX = 1;
178 b.scaleXDir = (Math.random() > 0.5 ? 1 : -1) * 0.02;
179 b.wobble = Math.random() * Math.PI * 2;
180 b.alive = true;
181 b.born = false;
182 b.birthTime = b.delay;
183 }
184 b.draw(ctx);
185 }
186
187 requestAnimationFrame(animate);
188}
189
190requestAnimationFrame(animate);
191
192window.addEventListener('resize', () => {
193 canvas.width = window.innerWidth;
194 canvas.height = window.innerHeight;
195});
196</script>
197</body>
198</html>