HI7RAI commited on
Commit
6b94f33
·
verified ·
1 Parent(s): 68e1f34

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +583 -19
index.html CHANGED
@@ -1,19 +1,583 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Cinematic Color Calibration & Light FX</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --primary-color: #00f2ff;
11
+ --bg-color: #0a0a0a;
12
+ --panel-bg: rgba(15, 15, 20, 0.75);
13
+ --text-color: #e0e0e0;
14
+ --accent-glow: 0 0 10px rgba(0, 242, 255, 0.5);
15
+ }
16
+
17
+ * {
18
+ margin: 0;
19
+ padding: 0;
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ body {
24
+ background-color: #000;
25
+ overflow: hidden;
26
+ font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
27
+ color: var(--text-color);
28
+ }
29
+
30
+ /* Canvas */
31
+ #stage {
32
+ position: fixed;
33
+ top: 0;
34
+ left: 0;
35
+ width: 100%;
36
+ height: 100%;
37
+ z-index: 1;
38
+ }
39
+
40
+ /* Header */
41
+ header {
42
+ position: fixed;
43
+ top: 20px;
44
+ left: 20px;
45
+ z-index: 100;
46
+ pointer-events: none;
47
+ text-transform: uppercase;
48
+ letter-spacing: 2px;
49
+ font-size: 0.8rem;
50
+ opacity: 0.7;
51
+ }
52
+
53
+ header a {
54
+ color: var(--primary-color);
55
+ text-decoration: none;
56
+ font-weight: bold;
57
+ text-shadow: 0 0 5px var(--primary-color);
58
+ pointer-events: auto;
59
+ transition: all 0.3s ease;
60
+ }
61
+
62
+ header a:hover {
63
+ text-shadow: 0 0 15px var(--primary-color);
64
+ letter-spacing: 3px;
65
+ }
66
+
67
+ /* Control Panel */
68
+ .controls-container {
69
+ position: fixed;
70
+ right: 20px;
71
+ top: 50%;
72
+ transform: translateY(-50%);
73
+ width: 300px;
74
+ background: var(--panel-bg);
75
+ backdrop-filter: blur(15px);
76
+ -webkit-backdrop-filter: blur(15px);
77
+ padding: 25px;
78
+ border-radius: 12px;
79
+ border: 1px solid rgba(255, 255, 255, 0.1);
80
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
81
+ z-index: 100;
82
+ transition: opacity 0.3s;
83
+ }
84
+
85
+ .controls-container:hover {
86
+ opacity: 1;
87
+ }
88
+
89
+ h2 {
90
+ font-size: 1.1rem;
91
+ margin-bottom: 20px;
92
+ color: var(--primary-color);
93
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
94
+ padding-bottom: 10px;
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 10px;
98
+ }
99
+
100
+ .control-group {
101
+ margin-bottom: 18px;
102
+ }
103
+
104
+ .label-row {
105
+ display: flex;
106
+ justify-content: space-between;
107
+ margin-bottom: 8px;
108
+ font-size: 0.85rem;
109
+ font-weight: 500;
110
+ }
111
+
112
+ .value-display {
113
+ color: var(--primary-color);
114
+ font-family: 'Courier New', monospace;
115
+ }
116
+
117
+ /* Custom Range Slider */
118
+ input[type=range] {
119
+ -webkit-appearance: none;
120
+ width: 100%;
121
+ background: transparent;
122
+ }
123
+
124
+ input[type=range]:focus {
125
+ outline: none;
126
+ }
127
+
128
+ input[type=range]::-webkit-slider-runnable-track {
129
+ width: 100%;
130
+ height: 4px;
131
+ cursor: pointer;
132
+ background: rgba(255, 255, 255, 0.2);
133
+ border-radius: 2px;
134
+ }
135
+
136
+ input[type=range]::-webkit-slider-thumb {
137
+ height: 16px;
138
+ width: 16px;
139
+ border-radius: 50%;
140
+ background: #fff;
141
+ cursor: pointer;
142
+ -webkit-appearance: none;
143
+ margin-top: -6px;
144
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
145
+ transition: transform 0.1s;
146
+ }
147
+
148
+ input[type=range]::-webkit-slider-thumb:hover {
149
+ transform: scale(1.2);
150
+ background: var(--primary-color);
151
+ }
152
+
153
+ /* Specific Styles for Firefox */
154
+ input[type=range]::-moz-range-track {
155
+ width: 100%;
156
+ height: 4px;
157
+ cursor: pointer;
158
+ background: rgba(255, 255, 255, 0.2);
159
+ border-radius: 2px;
160
+ }
161
+
162
+ input[type=range]::-moz-range-thumb {
163
+ height: 16px;
164
+ width: 16px;
165
+ border: none;
166
+ border-radius: 50%;
167
+ background: #fff;
168
+ cursor: pointer;
169
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
170
+ }
171
+
172
+ /* Section Toggles */
173
+ .section-title {
174
+ font-size: 0.7rem;
175
+ text-transform: uppercase;
176
+ color: #666;
177
+ margin-top: 20px;
178
+ margin-bottom: 10px;
179
+ letter-spacing: 1.5px;
180
+ }
181
+
182
+ .btn-group {
183
+ display: flex;
184
+ gap: 10px;
185
+ margin-bottom: 15px;
186
+ }
187
+
188
+ .toggle-btn {
189
+ flex: 1;
190
+ background: rgba(255, 255, 255, 0.05);
191
+ border: 1px solid rgba(255, 255, 255, 0.1);
192
+ color: #aaa;
193
+ padding: 8px;
194
+ border-radius: 4px;
195
+ cursor: pointer;
196
+ font-size: 0.8rem;
197
+ transition: all 0.2s;
198
+ }
199
+
200
+ .toggle-btn.active {
201
+ background: rgba(0, 242, 255, 0.1);
202
+ border-color: var(--primary-color);
203
+ color: var(--primary-color);
204
+ }
205
+
206
+ /* Responsive */
207
+ @media (max-width: 768px) {
208
+ .controls-container {
209
+ width: calc(100% - 40px);
210
+ top: auto;
211
+ bottom: 20px;
212
+ transform: none;
213
+ right: 20px;
214
+ left: 20px;
215
+ max-height: 40vh;
216
+ overflow-y: auto;
217
+ }
218
+ }
219
+ </style>
220
+ </head>
221
+ <body>
222
+
223
+ <canvas id="stage"></canvas>
224
+
225
+ <header>
226
+ <div>Video FX Engine</div>
227
+ <div style="margin-top: 5px; font-size: 0.7rem;">Interactive Light & Particle Simulation</div>
228
+ <div style="margin-top: 15px;">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a></div>
229
+ </header>
230
+
231
+ <div class="controls-container">
232
+ <h2><i class="fas fa-sliders-h"></i> Calibration Deck</h2>
233
+
234
+ <div class="section-title">Color Grading</div>
235
+
236
+ <div class="control-group">
237
+ <div class="label-row">
238
+ <span>Exposure (Dark)</span>
239
+ <span id="val-exposure" class="value-display">100%</span>
240
+ </div>
241
+ <input type="range" id="exposure" min="0" max="200" value="100">
242
+ </div>
243
+
244
+ <div class="control-group">
245
+ <div class="label-row">
246
+ <span>Contrast</span>
247
+ <span id="val-contrast" class="value-display">100%</span>
248
+ </div>
249
+ <input type="range" id="contrast" min="0" max="200" value="100">
250
+ </div>
251
+
252
+ <div class="control-group">
253
+ <div class="label-row">
254
+ <span>Saturation</span>
255
+ <span id="val-saturation" class="value-display">100%</span>
256
+ </div>
257
+ <input type="range" id="saturation" min="0" max="200" value="100">
258
+ </div>
259
+
260
+ <div class="section-title">Atmosphere & FX</div>
261
+
262
+ <div class="control-group">
263
+ <div class="label-row">
264
+ <span>Light Intensity (Max)</span>
265
+ <span id="val-intensity" class="value-display">50%</span>
266
+ </div>
267
+ <input type="range" id="intensity" min="0" max="100" value="50">
268
+ </div>
269
+
270
+ <div class="control-group">
271
+ <div class="label-row">
272
+ <span>Particles (Dust)</span>
273
+ <span id="val-particles" class="value-display">500</span>
274
+ </div>
275
+ <input type="range" id="particles" min="0" max="1500" value="500">
276
+ </div>
277
+
278
+ <div class="control-group">
279
+ <div class="label-row">
280
+ <span>Ray Length</span>
281
+ <span id="val-rays" class="value-display">Auto</span>
282
+ </div>
283
+ <input type="range" id="rays" min="0" max="100" value="60">
284
+ </div>
285
+
286
+ <div class="btn-group">
287
+ <button class="toggle-btn active" id="btn-dust">Dust</button>
288
+ <button class="toggle-btn active" id="btn-rays">Light Rays</button>
289
+ <button class="toggle-btn" id="btn-animate">Auto Rotate</button>
290
+ </div>
291
+
292
+ <div style="font-size: 0.7rem; color: #666; margin-top: 10px; text-align: center;">
293
+ Move mouse to control light source
294
+ </div>
295
+ </div>
296
+
297
+ <script>
298
+ /**
299
+ * Cinematic Video Calibration & Particle Engine
300
+ * Simulates color grading, volumetric lighting (raytracing fake),
301
+ * and floating dust particles.
302
+ */
303
+
304
+ const canvas = document.getElementById('stage');
305
+ const ctx = canvas.getContext('2d');
306
+
307
+ // State Management
308
+ const state = {
309
+ width: window.innerWidth,
310
+ height: window.innerHeight,
311
+ mouseX: window.innerWidth / 2,
312
+ mouseY: window.innerHeight / 3,
313
+
314
+ // Grading
315
+ exposure: 100,
316
+ contrast: 100,
317
+ saturation: 100,
318
+
319
+ // FX
320
+ intensity: 0.5,
321
+ rayLength: 0.6,
322
+ particleCount: 500,
323
+
324
+ // Toggles
325
+ showDust: true,
326
+ showRays: true,
327
+ autoRotate: false,
328
+
329
+ time: 0
330
+ };
331
+
332
+ // Resize Handler
333
+ function resize() {
334
+ state.width = window.innerWidth;
335
+ state.height = window.innerHeight;
336
+ canvas.width = state.width;
337
+ canvas.height = state.height;
338
+ }
339
+ window.addEventListener('resize', resize);
340
+ resize();
341
+
342
+ // Mouse Tracker
343
+ window.addEventListener('mousemove', (e) => {
344
+ state.mouseX = e.clientX;
345
+ state.mouseY = e.clientY;
346
+ });
347
+
348
+ // Touch Tracker
349
+ window.addEventListener('touchmove', (e) => {
350
+ state.mouseX = e.touches[0].clientX;
351
+ state.mouseY = e.touches[0].clientY;
352
+ });
353
+
354
+ // --- Particle System ---
355
+ class Particle {
356
+ constructor() {
357
+ this.reset(true);
358
+ }
359
+
360
+ reset(randomY = false) {
361
+ this.x = Math.random() * state.width;
362
+ this.y = randomY ? Math.random() * state.height : state.height + 10;
363
+ this.z = Math.random() * 2 + 0.5; // Depth simulation
364
+ this.size = Math.random() * 2;
365
+ this.speedY = (Math.random() * 0.5 + 0.2) * this.z;
366
+ this.speedX = (Math.random() - 0.5) * 0.2;
367
+ this.opacity = Math.random() * 0.5 + 0.1;
368
+ }
369
+
370
+ update() {
371
+ this.y -= this.speedY;
372
+ this.x += Math.sin(state.time * 0.05 + this.y * 0.01) * 0.2; // Wavy motion
373
+
374
+ // Mouse repulsion/attraction
375
+ const dx = this.x - state.mouseX;
376
+ const dy = this.y - state.mouseY;
377
+ const dist = Math.sqrt(dx*dx + dy*dy);
378
+ if (dist < 200) {
379
+ this.x += dx * 0.01;
380
+ this.y += dy * 0.01;
381
+ }
382
+
383
+ if (this.y < -10) {
384
+ this.reset();
385
+ }
386
+ }
387
+
388
+ draw() {
389
+ ctx.beginPath();
390
+ ctx.arc(this.x, this.y, this.size * this.z, 0, Math.PI * 2);
391
+ ctx.fillStyle = `rgba(220, 220, 200, ${this.opacity * state.intensity})`;
392
+ ctx.fill();
393
+ }
394
+ }
395
+
396
+ let particles = [];
397
+ function initParticles() {
398
+ particles = [];
399
+ for (let i = 0; i < 2000; i++) { // Max pool
400
+ particles.push(new Particle());
401
+ }
402
+ }
403
+ initParticles();
404
+
405
+ // --- Volumetric Light Rays (God Rays) ---
406
+ function drawLightRays() {
407
+ if (!state.showRays) return;
408
+
409
+ const cx = state.mouseX;
410
+ const cy = state.mouseY;
411
+ const rays = 12;
412
+ const maxRadius = Math.max(state.width, state.height) * state.rayLength * 1.5;
413
+
414
+ ctx.save();
415
+ ctx.globalCompositeOperation = 'screen'; // Additive blending for light
416
+
417
+ // Base glow
418
+ const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, maxRadius);
419
+ gradient.addColorStop(0, `rgba(255, 255, 240, ${state.intensity * 0.8})`);
420
+ gradient.addColorStop(0.1, `rgba(200, 220, 255, ${state.intensity * 0.3})`);
421
+ gradient.addColorStop(0.5, `rgba(100, 150, 255, ${state.intensity * 0.1})`);
422
+ gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
423
+
424
+ ctx.fillStyle = gradient;
425
+ ctx.fillRect(0, 0, state.width, state.height);
426
+
427
+ // Rotating Shafts
428
+ if (state.autoRotate) state.time += 0.002;
429
+
430
+ for (let i = 0; i < rays; i++) {
431
+ const angle = (Math.PI * 2 / rays) * i + state.time;
432
+ ctx.translate(cx, cy);
433
+ ctx.rotate(angle);
434
+
435
+ // Draw Ray
436
+ ctx.beginPath();
437
+ ctx.moveTo(0, 0);
438
+ // Widen the ray as it goes out
439
+ const widthSpread = 150 + Math.sin(i + state.time * 5) * 50;
440
+ ctx.lineTo(maxRadius, -widthSpread / 2);
441
+ ctx.lineTo(maxRadius, widthSpread / 2);
442
+ ctx.closePath();
443
+
444
+ // Gradient for ray
445
+ const rayGrad = ctx.createLinearGradient(0, 0, maxRadius, 0);
446
+ rayGrad.addColorStop(0, `rgba(255, 255, 255, ${state.intensity * 0.4})`);
447
+ rayGrad.addColorStop(0.2, `rgba(200, 220, 255, ${state.intensity * 0.1})`);
448
+ rayGrad.addColorStop(1, 'rgba(0, 0, 0, 0)');
449
+
450
+ ctx.fillStyle = rayGrad;
451
+ ctx.fill();
452
+
453
+ // Reset matrix
454
+ ctx.rotate(-angle);
455
+ ctx.translate(-cx, -cy);
456
+ }
457
+
458
+ ctx.restore();
459
+ }
460
+
461
+ // --- Main Render Loop ---
462
+ function animate() {
463
+ requestAnimationFrame(animate);
464
+ state.time += 1;
465
+
466
+ // 1. Clear and apply Grading Filters to the WHOLE context
467
+ // We use CSS filters on the context for "Global" grading
468
+ const brightness = state.exposure / 100;
469
+ const contrast = state.contrast / 100;
470
+ const saturate = state.saturation / 100;
471
+
472
+ // Note: Canvas API filter support is good in modern browsers
473
+ ctx.filter = `brightness(${brightness}) contrast(${contrast}) saturate(${saturate})`;
474
+
475
+ // Fill Background (Dark Room)
476
+ // Create a very subtle vignette
477
+ const bgGradient = ctx.createRadialGradient(state.width/2, state.height/2, 100, state.width/2, state.height/2, state.height);
478
+ bgGradient.addColorStop(0, '#1a1a20'); // Dark Blue-Grey
479
+ bgGradient.addColorStop(1, '#050505'); // Almost Black
480
+
481
+ ctx.fillStyle = bgGradient;
482
+ ctx.fillRect(0, 0, state.width, state.height);
483
+
484
+ // 2. Draw Volumetric Light (Behind dust)
485
+ // We use 'source-over' normally, but for rays we might want screen.
486
+ // However, applying the filter above makes everything graded.
487
+ // To make rays look "colored" before grading, we draw them, then apply filter?
488
+ // Actually, ctx.filter applies to drawing operations.
489
+ // But if we want the grading to affect everything uniformly, we keep it.
490
+
491
+ // However, let's draw Rays without the filter first to keep them "hot",
492
+ // then draw background/dust, then apply filter?
493
+ // For simplicity and performance in this single-file demo, we apply grading to everything.
494
+
495
+ drawLightRays();
496
+
497
+ // 3. Draw Light Source Body (The "Sun" or Bulb)
498
+ ctx.filter = 'none'; // Turn off filter for the source core to make it pop
499
+ const lx = state.mouseX;
500
+ const ly = state.mouseY;
501
+
502
+ const lightGrad = ctx.createRadialGradient(lx, ly, 0, lx, ly, 60 * state.intensity + 20);
503
+ lightGrad.addColorStop(0, 'rgba(255, 255, 255, 1)');
504
+ lightGrad.addColorStop(0.2, 'rgba(240, 245, 255, 0.8)');
505
+ lightGrad.addColorStop(1, 'rgba(0, 0, 0, 0)');
506
+
507
+ ctx.fillStyle = lightGrad;
508
+ ctx.beginPath();
509
+ ctx.arc(lx, ly, 60, 0, Math.PI*2);
510
+ ctx.fill();
511
+
512
+ // 4. Draw Particles (Dust)
513
+ // Particles are affected by the global filter if we don't reset,
514
+ // which helps integrate them into the "scene".
515
+ // Let's re-enable filter for dust to tint them based on grading
516
+ ctx.filter = `brightness(${brightness}) contrast(${contrast}) saturate(${saturate})`;
517
+
518
+ if (state.showDust) {
519
+ for (let i = 0; i < state.particleCount; i++) {
520
+ if (particles[i]) {
521
+ particles[i].update();
522
+ particles[i].draw();
523
+ }
524
+ }
525
+ }
526
+
527
+ // 5. Post-Processing Overlay (Vignette & Noise)
528
+ // We apply this ON TOP of everything using a temporary canvas or just drawing over
529
+ ctx.filter = 'none';
530
+
531
+ // Draw a vignette overlay to darken corners
532
+ const vigGrad = ctx.createRadialGradient(state.width/2, state.height/2, state.height/3, state.width/2, state.height/2, state.height);
533
+ vigGrad.addColorStop(0, 'rgba(0,0,0,0)');
534
+ vigGrad.addColorStop(1, 'rgba(0,0,0,0.6)');
535
+ ctx.fillStyle = vigGrad;
536
+ ctx.fillRect(0, 0, state.width, state.height);
537
+
538
+ // Scanlines / Grid effect (Subtle)
539
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
540
+ for(let y = 0; y < state.height; y+=4) {
541
+ ctx.fillRect(0, y, state.width, 1);
542
+ }
543
+ }
544
+
545
+ // --- UI Logic ---
546
+ const bindInput = (id, key, suffix = '%') => {
547
+ const input = document.getElementById(id);
548
+ const display = document.getElementById(`val-${id}`);
549
+ input.addEventListener('input', (e) => {
550
+ state[key] = parseFloat(e.target.value);
551
+ display.innerText = state[key] + suffix;
552
+ });
553
+ };
554
+
555
+ bindInput('exposure', 'exposure');
556
+ bindInput('contrast', 'contrast');
557
+ bindInput('saturation', 'saturation');
558
+ bindInput('intensity', 'intensity', '');
559
+ bindInput('particles', 'particleCount');
560
+ bindInput('rays', 'rayLength');
561
+
562
+ // Toggles
563
+ document.getElementById('btn-dust').addEventListener('click', (e) => {
564
+ state.showDust = !state.showDust;
565
+ e.target.classList.toggle('active');
566
+ });
567
+
568
+ document.getElementById('btn-rays').addEventListener('click', (e) => {
569
+ state.showRays = !state.showRays;
570
+ e.target.classList.toggle('active');
571
+ });
572
+
573
+ document.getElementById('btn-animate').addEventListener('click', (e) => {
574
+ state.autoRotate = !state.autoRotate;
575
+ e.target.classList.toggle('active');
576
+ });
577
+
578
+ // Start
579
+ animate();
580
+
581
+ </script>
582
+ </body>
583
+ </html>