mlmihjaz commited on
Commit
9c109bf
·
verified ·
1 Parent(s): 00e34e9

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +770 -553
index.html CHANGED
@@ -1,581 +1,798 @@
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>Alphabet Pop! Interactive Learning Game</title>
7
-
8
- <!-- Import Google Fonts for a playful look -->
9
- <link rel="preconnect" href="https://fonts.googleapis.com">
10
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Nunito:wght@400;700&display=swap" rel="stylesheet">
12
-
13
- <!-- FontAwesome for Icons -->
14
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
15
-
16
- <style>
17
- :root {
18
- --primary-color: #6C63FF;
19
- --secondary-color: #FF6584;
20
- --accent-color: #43D9AD;
21
- --bg-gradient: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
22
- --glass-bg: rgba(255, 255, 255, 0.85);
23
- --glass-border: rgba(255, 255, 255, 0.5);
24
- --text-dark: #2D3436;
25
- --shadow-lg: 0 10px 30px rgba(0,0,0,0.15);
26
- }
27
-
28
- * {
29
- box-sizing: border-box;
30
- margin: 0;
31
- padding: 0;
32
- }
33
-
34
- body {
35
- font-family: 'Nunito', sans-serif;
36
- background: var(--bg-gradient);
37
- height: 100vh;
38
- display: flex;
39
- flex-direction: column;
40
- overflow: hidden; /* Prevent scrolling during animations */
41
- color: var(--text-dark);
42
- }
43
-
44
- /* Header Section */
45
- header {
46
- padding: 1rem 2rem;
47
- display: flex;
48
- justify-content: space-between;
49
- align-items: center;
50
- background: rgba(255, 255, 255, 0.9);
51
- box-shadow: 0 2px 10px rgba(0,0,0,0.05);
52
- z-index: 100;
53
- }
54
 
55
- .logo {
56
- font-family: 'Fredoka', sans-serif;
57
- font-size: 1.5rem;
58
- font-weight: 700;
59
- color: var(--primary-color);
60
- display: flex;
61
- align-items: center;
62
- gap: 10px;
63
- }
64
-
65
- .anycoder-link {
66
- font-size: 0.9rem;
67
- font-weight: 600;
68
- color: var(--text-dark);
69
- text-decoration: none;
70
- padding: 0.5rem 1rem;
71
- background: #f0f0f0;
72
- border-radius: 20px;
73
- transition: all 0.3s ease;
74
- }
75
-
76
- .anycoder-link:hover {
77
- background: var(--primary-color);
78
- color: white;
79
- transform: translateY(-2px);
80
- }
81
-
82
- /* Main Game Area */
83
- main {
84
- flex: 1;
85
- display: flex;
86
- flex-direction: column;
87
- justify-content: center;
88
- align-items: center;
89
- position: relative;
90
- padding: 1rem;
91
- }
92
-
93
- /* Game Container (Glassmorphism) */
94
- .game-board {
95
- background: var(--glass-bg);
96
- backdrop-filter: blur(12px);
97
- -webkit-backdrop-filter: blur(12px);
98
- border: 1px solid var(--glass-border);
99
- border-radius: 30px;
100
- padding: 2rem;
101
- width: 90%;
102
- max-width: 600px;
103
- box-shadow: var(--shadow-lg);
104
- text-align: center;
105
- display: flex;
106
- flex-direction: column;
107
- align-items: center;
108
- min-height: 500px;
109
- justify-content: space-between;
110
- transition: transform 0.3s ease;
111
- }
112
-
113
- /* Progress Bar */
114
- .progress-container {
115
- width: 100%;
116
- height: 10px;
117
- background: #e0e0e0;
118
- border-radius: 10px;
119
- margin-bottom: 2rem;
120
- overflow: hidden;
121
- }
122
-
123
- .progress-bar {
124
- height: 100%;
125
- width: 0%;
126
- background: linear-gradient(90deg, var(--secondary-color), var(--primary-color));
127
- border-radius: 10px;
128
- transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
129
- }
130
-
131
- /* The Big Letter Display */
132
- .letter-display {
133
- font-family: 'Fredoka', sans-serif;
134
- font-size: 12rem; /* Responsive size handled below */
135
- font-weight: 700;
136
- color: var(--primary-color);
137
- text-shadow: 4px 4px 0px rgba(0,0,0,0.1);
138
- cursor: default;
139
- user-select: none;
140
- position: relative;
141
- z-index: 2;
142
- transition: color 0.3s;
143
- }
144
-
145
- /* Image Container */
146
- .image-container {
147
- width: 200px;
148
- height: 200px;
149
- border-radius: 20px;
150
- overflow: hidden;
151
- background: #fff;
152
- box-shadow: inset 0 0 20px rgba(0,0,0,0.05);
153
- border: 4px solid white;
154
- display: flex;
155
- justify-content: center;
156
- align-items: center;
157
- opacity: 0; /* Hidden by default */
158
- transform: scale(0.8);
159
- transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
160
- margin-top: 1rem;
161
- }
162
-
163
- .image-container.visible {
164
- opacity: 1;
165
- transform: scale(1);
166
- }
167
-
168
- .image-container img {
169
- width: 100%;
170
- height: 100%;
171
- object-fit: cover;
172
- }
173
-
174
- /* Instructions / Status */
175
- .status-text {
176
- font-size: 1.2rem;
177
- color: #636e72;
178
- margin-top: 1rem;
179
- font-weight: 600;
180
- }
181
-
182
- .hint-text {
183
- font-size: 0.9rem;
184
- color: #b2bec3;
185
- margin-top: 0.5rem;
186
- }
187
-
188
- /* Controls (Start/Restart) */
189
- .controls {
190
- margin-top: 2rem;
191
- }
192
-
193
- .btn {
194
- background: var(--primary-color);
195
- color: white;
196
- border: none;
197
- padding: 1rem 2.5rem;
198
- font-size: 1.2rem;
199
- border-radius: 50px;
200
- cursor: pointer;
201
- font-family: 'Fredoka', sans-serif;
202
- box-shadow: 0 5px 15px rgba(108, 99, 255, 0.4);
203
- transition: all 0.2s ease;
204
- display: flex;
205
- align-items: center;
206
- gap: 10px;
207
- }
208
-
209
- .btn:hover {
210
- transform: translateY(-3px);
211
- box-shadow: 0 8px 20px rgba(108, 99, 255, 0.5);
212
- }
213
-
214
- .btn:active {
215
- transform: translateY(1px);
216
- }
217
-
218
- .btn.secondary {
219
- background: var(--text-dark);
220
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
221
- }
222
-
223
- /* Animations */
224
- @keyframes popIn {
225
- 0% { transform: scale(0); opacity: 0; }
226
- 60% { transform: scale(1.2); opacity: 1; }
227
- 100% { transform: scale(1); opacity: 1; }
228
- }
229
-
230
- @keyframes shake {
231
- 0% { transform: translateX(0); }
232
- 25% { transform: translateX(-10px) rotate(-5deg); color: var(--secondary-color); }
233
- 50% { transform: translateX(10px) rotate(5deg); color: var(--secondary-color); }
234
- 75% { transform: translateX(-10px) rotate(-5deg); }
235
- 100% { transform: translateX(0); color: var(--primary-color); }
236
- }
237
-
238
- @keyframes float {
239
- 0% { transform: translateY(0px); }
240
- 50% { transform: translateY(-10px); }
241
- 100% { transform: translateY(0px); }
242
- }
243
-
244
- .anim-pop {
245
- animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
246
- }
247
-
248
- .anim-shake {
249
- animation: shake 0.4s ease-in-out;
250
- }
251
-
252
- .anim-float {
253
- animation: float 3s ease-in-out infinite;
254
- }
255
-
256
- /* Confetti Overlay */
257
- #confetti-canvas {
258
- position: absolute;
259
- top: 0;
260
- left: 0;
261
- width: 100%;
262
- height: 100%;
263
- pointer-events: none;
264
- z-index: 50;
265
- }
266
-
267
- /* Responsive Design */
268
- @media (max-width: 768px) {
269
- .letter-display {
270
- font-size: 8rem;
271
- }
272
- .game-board {
273
- width: 95%;
274
- padding: 1.5rem;
275
- }
276
- .image-container {
277
- width: 150px;
278
- height: 150px;
279
- }
280
- }
281
-
282
- @media (max-width: 480px) {
283
- .letter-display {
284
- font-size: 6rem;
285
- }
286
- header {
287
- padding: 0.8rem 1rem;
288
- }
289
- .logo span {
290
- display: none; /* Hide text logo on very small screens, keep icon */
291
- }
292
- .image-container {
293
- width: 120px;
294
- height: 120px;
295
- }
296
- }
297
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  </head>
 
299
  <body>
300
 
301
- <header>
302
- <div class="logo">
303
- <i class="fa-solid fa-shapes"></i>
304
- <span>AlphaPop</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  </div>
306
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
307
- Built with anycoder
308
- </a>
309
- </header>
310
-
311
- <main>
312
- <canvas id="confetti-canvas"></canvas>
313
-
314
- <div class="game-board">
315
- <!-- Progress -->
316
- <div class="progress-container">
317
- <div class="progress-bar" id="progressBar"></div>
318
- </div>
319
-
320
- <!-- Interactive Area -->
321
- <div id="gameArea" style="display: none; flex-direction: column; align-items: center; width: 100%;">
322
- <div class="letter-display anim-pop" id="letterDisplay">A</div>
323
-
324
- <div class="image-container" id="imageContainer">
325
- <img id="letterImage" src="" alt="Letter Image">
326
- </div>
327
-
328
- <div class="status-text" id="statusText">Press the 'A' key!</div>
329
- <div class="hint-text"><i class="fa-solid fa-volume-high"></i> Sound On</div>
330
- </div>
331
-
332
- <!-- Start Screen -->
333
- <div id="startScreen" style="text-align: center;">
334
- <div class="logo" style="justify-content: center; font-size: 3rem; margin-bottom: 1rem; color: var(--primary-color);">
335
- <i class="fa-solid fa-keyboard"></i>
336
- </div>
337
- <h2 style="margin-bottom: 1rem; font-size: 1.8rem;">Are you ready to type?</h2>
338
- <p style="margin-bottom: 2rem; color: #636e72;">Press the matching key on your keyboard to hear the sound and see the picture!</p>
339
- <button class="btn" onclick="startGame()">
340
- Start Game <i class="fa-solid fa-play"></i>
341
- </button>
342
- </div>
343
-
344
- <!-- End Screen -->
345
- <div id="endScreen" style="display: none; text-align: center;">
346
- <i class="fa-solid fa-trophy" style="font-size: 4rem; color: #f1c40f; margin-bottom: 1rem; animation: float 2s infinite;"></i>
347
- <h2 style="margin-bottom: 1rem; font-size: 2rem;">Great Job!</h2>
348
- <p style="margin-bottom: 2rem; color: #636e72;">You completed the alphabet.</p>
349
- <button class="btn" onclick="startGame()">
350
- Play Again <i class="fa-solid fa-rotate-right"></i>
351
- </button>
352
- </div>
353
  </div>
354
- </main>
355
-
356
- <script>
357
- // Game Configuration
358
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('');
359
- let currentIndex = 0;
360
- let isGameActive = false;
361
-
362
- // DOM Elements
363
- const startScreen = document.getElementById('startScreen');
364
- const endScreen = document.getElementById('endScreen');
365
- const gameArea = document.getElementById('gameArea');
366
- const letterDisplay = document.getElementById('letterDisplay');
367
- const statusText = document.getElementById('statusText');
368
- const imageContainer = document.getElementById('imageContainer');
369
- const letterImage = document.getElementById('letterImage');
370
- const progressBar = document.getElementById('progressBar');
371
-
372
- // --- USER CONFIGURATION FOR IMAGES ---
373
- // If you have local images, replace the return value of this function
374
- // with the path to your image file.
375
- // Example: return `assets/images/${letter}.jpg`;
376
- function getRandomImageForLetter(letter) {
377
- // This generates a consistent random seed based on the letter + a random number
378
- // to simulate having multiple images for the same letter.
379
- const randomId = Math.floor(Math.random() * 1000);
380
-
381
- // Using picsum.photos as a placeholder service.
382
- // The seed ensures that 'A' always looks somewhat different but consistent per session.
383
- return `https://picsum.photos/seed/${letter}${randomId}/300/300`;
384
-
385
- // YOUR CODE (Uncomment below and adjust path if you have local files):
386
- // const images = {
387
- // 'A': ['apple.jpg', 'ant.jpg', 'axe.jpg'],
388
- // 'B': ['ball.jpg', 'bear.jpg', 'boat.jpg'],
389
- // // ... add all letters
390
- // };
391
- // const list = images[letter] || ['default.jpg'];
392
- // return 'path/to/folder/' + list[Math.floor(Math.random() * list.length)];
393
- }
394
-
395
- // Sound Functionality (Web Speech API)
396
- function speakLetter(letter) {
397
- if ('speechSynthesis' in window) {
398
- // Cancel any currently playing speech
399
- window.speechSynthesis.cancel();
400
-
401
- const utterance = new SpeechSynthesisUtterance(letter);
402
- utterance.lang = 'en-US';
403
- utterance.rate = 0.8; // Slightly slower for kids
404
- utterance.pitch = 1.1; // Slightly higher/friendly
405
- window.speechSynthesis.speak(utterance);
406
- }
407
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
- // Game Logic
410
- function startGame() {
411
- currentIndex = 0;
412
- isGameActive = true;
413
-
414
- // UI Transitions
415
- startScreen.style.display = 'none';
416
- endScreen.style.display = 'none';
417
- gameArea.style.display = 'flex';
418
-
419
- updateLevel();
420
  }
421
 
422
- function updateLevel() {
423
- if (currentIndex >= alphabet.length) {
424
- endGame();
425
- return;
426
- }
427
-
428
- const currentLetter = alphabet[currentIndex];
429
-
430
- // Reset Animations
431
- letterDisplay.classList.remove('anim-pop', 'anim-shake');
432
- void letterDisplay.offsetWidth; // Trigger reflow to restart animation
433
- letterDisplay.classList.add('anim-pop');
434
-
435
- // Update Text
436
- letterDisplay.textContent = currentLetter;
437
- statusText.textContent = `Press the '${currentLetter}' key!`;
438
-
439
- // Hide Image initially
440
- imageContainer.classList.remove('visible');
441
-
442
- // Update Progress
443
- const progress = (currentIndex / alphabet.length) * 100;
444
- progressBar.style.width = `${progress}%`;
445
-
446
- // Speak the letter immediately when level starts
447
- speakLetter(currentLetter);
448
- }
449
 
450
- function handleInput(e) {
451
- if (!isGameActive) return;
452
 
453
- const key = e.key.toUpperCase();
454
- const targetLetter = alphabet[currentIndex];
 
455
 
456
- // Check if the key pressed is a letter
457
- if (!/^[A-Z]$/.test(key)) return;
458
 
459
- if (key === targetLetter) {
460
- // Correct Answer
461
- onCorrectPress(targetLetter);
462
- } else {
463
- // Wrong Answer
464
- onWrongPress();
465
- }
466
- }
467
 
468
- function onCorrectPress(letter) {
469
- // Play sound again for confirmation
470
- speakLetter(letter);
471
-
472
- // Show Image
473
- const imgUrl = getRandomImageForLetter(letter);
474
- letterImage.src = imgUrl;
475
-
476
- // Wait for image to load slightly before showing to prevent flicker
477
- letterImage.onload = () => {
478
- imageContainer.classList.add('visible');
479
- };
480
- if(letterImage.complete) imageContainer.classList.add('visible');
481
-
482
- // Visual feedback on text
483
- letterDisplay.style.color = "var(--accent-color)";
484
- statusText.textContent = "Great!";
485
- statusText.style.color = "var(--accent-color)";
486
-
487
- // Trigger small confetti
488
- fireConfetti(0.5); // 50% screen width
489
-
490
- // Wait 1.5 seconds then go to next letter
491
- setTimeout(() => {
492
- currentIndex++;
493
- letterDisplay.style.color = "var(--primary-color)";
494
- statusText.style.color = "#636e72";
495
- updateLevel();
496
- }, 1500);
497
- }
498
 
499
- function onWrongPress() {
500
- // Shake animation
501
- letterDisplay.classList.remove('anim-shake');
502
- void letterDisplay.offsetWidth;
503
- letterDisplay.classList.add('anim-shake');
504
-
505
- statusText.textContent = "Oops, try again!";
506
- statusText.style.color = "var(--secondary-color)";
507
-
508
- // Play error sound (using speech synthesis for a low 'uh-oh' or just re-prompting target)
509
- // For simplicity, we just re-speak the target letter
510
- speakLetter(alphabet[currentIndex]);
511
  }
 
512
 
513
- function endGame() {
514
- isGameActive = false;
515
- gameArea.style.display = 'none';
516
- endScreen.style.display = 'block';
517
- progressBar.style.width = '100%';
518
- fireConfetti(1.0); // Full screen confetti
519
- }
 
 
 
 
 
520
 
521
- // Event Listeners
522
- window.addEventListener('keydown', handleInput);
 
523
 
524
- // --- Simple Confetti Implementation (No external library) ---
525
- const canvas = document.getElementById('confetti-canvas');
526
- const ctx = canvas.getContext('2d');
527
- let particles = [];
528
 
529
- function resizeCanvas() {
530
- canvas.width = window.innerWidth;
531
- canvas.height = window.innerHeight;
532
- }
533
- window.addEventListener('resize', resizeCanvas);
534
- resizeCanvas();
 
535
 
536
- function fireConfetti(intensity = 1.0) {
537
- const particleCount = 100 * intensity;
538
- const colors = ['#FF6584', '#6C63FF', '#43D9AD', '#FFD93D', '#FF8C00'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
- for (let i = 0; i < particleCount; i++) {
541
- particles.push({
542
- x: canvas.width / 2,
543
- y: canvas.height / 2 + 50, // Start from middle-ish
544
- vx: (Math.random() - 0.5) * 20 * intensity,
545
- vy: (Math.random() - 1) * 20 * intensity,
546
- size: Math.random() * 8 + 4,
547
- color: colors[Math.floor(Math.random() * colors.length)],
548
- life: 100,
549
- gravity: 0.5
550
- });
551
- }
552
- requestAnimationFrame(updateConfetti);
553
  }
554
-
555
- function updateConfetti() {
556
- ctx.clearRect(0, 0, canvas.width, canvas.height);
557
-
558
- for (let i = 0; i < particles.length; i++) {
559
- let p = particles[i];
560
- p.x += p.vx;
561
- p.y += p.vy;
562
- p.vy += p.gravity;
563
- p.life--;
564
-
565
- ctx.fillStyle = p.color;
566
- ctx.beginPath();
567
- ctx.rect(p.x, p.y, p.size, p.size);
568
- ctx.fill();
569
- }
570
-
571
- // Remove dead particles
572
- particles = particles.filter(p => p.life > 0);
573
-
574
- if (particles.length > 0) {
575
- requestAnimationFrame(updateConfetti);
576
- }
577
  }
578
-
579
- </script>
580
  </body>
 
581
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>AlphaPop & Chat - Interactive Learning</title>
8
+
9
+ <!-- Import Google Fonts -->
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;600;700&family=Nunito:wght@400;700&display=swap"
13
+ rel="stylesheet">
14
+
15
+ <!-- FontAwesome for Icons -->
16
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
17
+
18
+ <style>
19
+ :root {
20
+ --primary-color: #6C63FF;
21
+ --secondary-color: #FF6584;
22
+ --accent-color: #43D9AD;
23
+ --bg-gradient: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
24
+ --glass-bg: rgba(255, 255, 255, 0.9);
25
+ --glass-border: rgba(255, 255, 255, 0.5);
26
+ --text-dark: #2D3436;
27
+ --shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.15);
28
+ --chat-bg: #ffffff;
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ margin: 0;
34
+ padding: 0;
35
+ }
36
+
37
+ body {
38
+ font-family: 'Nunito', sans-serif;
39
+ background: var(--bg-gradient);
40
+ height: 100vh;
41
+ display: flex;
42
+ flex-direction: column;
43
+ overflow: hidden;
44
+ color: var(--text-dark);
45
+ }
46
+
47
+ /* Header Section */
48
+ header {
49
+ padding: 1rem 2rem;
50
+ display: flex;
51
+ justify-content: space-between;
52
+ align-items: center;
53
+ background: rgba(255, 255, 255, 0.95);
54
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
55
+ z-index: 100;
56
+ }
57
+
58
+ .logo {
59
+ font-family: 'Fredoka', sans-serif;
60
+ font-size: 1.5rem;
61
+ font-weight: 700;
62
+ color: var(--primary-color);
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 10px;
66
+ }
67
+
68
+ .header-actions {
69
+ display: flex;
70
+ gap: 15px;
71
+ align-items: center;
72
+ }
73
+
74
+ .chat-trigger {
75
+ background: var(--primary-color);
76
+ color: white;
77
+ border: none;
78
+ width: 40px;
79
+ height: 40px;
80
+ border-radius: 50%;
81
+ cursor: pointer;
82
+ display: flex;
83
+ justify-content: center;
84
+ align-items: center;
85
+ transition: transform 0.2s;
86
+ box-shadow: 0 4px 10px rgba(108, 99, 255, 0.3);
87
+ }
88
+
89
+ .chat-trigger:hover {
90
+ transform: scale(1.1);
91
+ }
92
+
93
+ .anycoder-link {
94
+ font-size: 0.9rem;
95
+ font-weight: 600;
96
+ color: var(--text-dark);
97
+ text-decoration: none;
98
+ padding: 0.5rem 1rem;
99
+ background: #f0f0f0;
100
+ border-radius: 20px;
101
+ transition: all 0.3s ease;
102
+ }
103
+
104
+ .anycoder-link:hover {
105
+ background: var(--primary-color);
106
+ color: white;
107
+ transform: translateY(-2px);
108
+ }
109
+
110
+ /* Main Game Area */
111
+ main {
112
+ flex: 1;
113
+ display: flex;
114
+ flex-direction: column;
115
+ justify-content: center;
116
+ align-items: center;
117
+ position: relative;
118
+ padding: 1rem;
119
+ }
120
+
121
+ /* Game Container */
122
+ .game-board {
123
+ background: var(--glass-bg);
124
+ backdrop-filter: blur(12px);
125
+ -webkit-backdrop-filter: blur(12px);
126
+ border: 1px solid var(--glass-border);
127
+ border-radius: 30px;
128
+ padding: 2rem;
129
+ width: 90%;
130
+ max-width: 600px;
131
+ box-shadow: var(--shadow-lg);
132
+ text-align: center;
133
+ display: flex;
134
+ flex-direction: column;
135
+ align-items: center;
136
+ min-height: 500px;
137
+ justify-content: space-between;
138
+ transition: transform 0.3s ease, filter 0.3s ease;
139
+ }
140
+
141
+ .game-board.blurred {
142
+ filter: blur(5px);
143
+ pointer-events: none;
144
+ }
145
+
146
+ /* Progress Bar */
147
+ .progress-container {
148
+ width: 100%;
149
+ height: 10px;
150
+ background: #e0e0e0;
151
+ border-radius: 10px;
152
+ margin-bottom: 2rem;
153
+ overflow: hidden;
154
+ }
155
+
156
+ .progress-bar {
157
+ height: 100%;
158
+ width: 0%;
159
+ background: linear-gradient(90deg, var(--secondary-color), var(--primary-color));
160
+ border-radius: 10px;
161
+ transition: width 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
162
+ }
163
+
164
+ /* Letter Display */
165
+ .letter-display {
166
+ font-family: 'Fredoka', sans-serif;
167
+ font-size: 10rem;
168
+ font-weight: 700;
169
+ color: var(--primary-color);
170
+ text-shadow: 4px 4px 0px rgba(0, 0, 0, 0.1);
171
+ cursor: default;
172
+ user-select: none;
173
+ position: relative;
174
+ z-index: 2;
175
+ transition: color 0.3s;
176
+ }
177
+
178
+ /* Image Container */
179
+ .image-container {
180
+ width: 180px;
181
+ height: 180px;
182
+ border-radius: 20px;
183
+ overflow: hidden;
184
+ background: #fff;
185
+ box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.05);
186
+ border: 4px solid white;
187
+ display: flex;
188
+ justify-content: center;
189
+ align-items: center;
190
+ opacity: 0;
191
+ transform: scale(0.8);
192
+ transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
193
+ margin-top: 1rem;
194
+ }
195
+
196
+ .image-container.visible {
197
+ opacity: 1;
198
+ transform: scale(1);
199
+ }
200
+
201
+ .image-container img {
202
+ width: 100%;
203
+ height: 100%;
204
+ object-fit: cover;
205
+ }
206
+
207
+ /* Status Text */
208
+ .status-text {
209
+ font-size: 1.2rem;
210
+ color: #636e72;
211
+ margin-top: 1rem;
212
+ font-weight: 600;
213
+ }
214
+
215
+ .hint-text {
216
+ font-size: 0.9rem;
217
+ color: #b2bec3;
218
+ margin-top: 0.5rem;
219
+ }
220
+
221
+ /* Buttons */
222
+ .controls {
223
+ margin-top: 2rem;
224
+ }
225
+
226
+ .btn {
227
+ background: var(--primary-color);
228
+ color: white;
229
+ border: none;
230
+ padding: 1rem 2.5rem;
231
+ font-size: 1.2rem;
232
+ border-radius: 50px;
233
+ cursor: pointer;
234
+ font-family: 'Fredoka', sans-serif;
235
+ box-shadow: 0 5px 15px rgba(108, 99, 255, 0.4);
236
+ transition: all 0.2s ease;
237
+ display: inline-flex;
238
+ align-items: center;
239
+ gap: 10px;
240
+ }
241
+
242
+ .btn:hover {
243
+ transform: translateY(-3px);
244
+ box-shadow: 0 8px 20px rgba(108, 99, 255, 0.5);
245
+ }
246
+
247
+ .btn:active {
248
+ transform: translateY(1px);
249
+ }
250
+
251
+ /* --- CHAT UI STYLES --- */
252
+ .chat-overlay {
253
+ position: fixed;
254
+ top: 0;
255
+ left: 0;
256
+ width: 100%;
257
+ height: 100%;
258
+ background: rgba(0, 0, 0, 0.4);
259
+ z-index: 200;
260
+ display: flex;
261
+ justify-content: center;
262
+ align-items: center;
263
+ opacity: 0;
264
+ visibility: hidden;
265
+ transition: all 0.3s ease;
266
+ }
267
+
268
+ .chat-overlay.active {
269
+ opacity: 1;
270
+ visibility: visible;
271
+ }
272
+
273
+ .chat-window {
274
+ width: 90%;
275
+ max-width: 500px;
276
+ height: 70vh;
277
+ background: white;
278
+ border-radius: 20px;
279
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
280
+ display: flex;
281
+ flex-direction: column;
282
+ overflow: hidden;
283
+ transform: translateY(50px);
284
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
285
+ }
286
+
287
+ .chat-overlay.active .chat-window {
288
+ transform: translateY(0);
289
+ }
290
+
291
+ .chat-header {
292
+ background: var(--primary-color);
293
+ color: white;
294
+ padding: 1rem;
295
+ display: flex;
296
+ align-items: center;
297
+ justify-content: space-between;
298
+ }
299
+
300
+ .chat-header h3 {
301
+ font-family: 'Fredoka', sans-serif;
302
+ display: flex;
303
+ align-items: center;
304
+ gap: 10px;
305
+ }
306
+
307
+ .close-chat {
308
+ background: none;
309
+ border: none;
310
+ color: white;
311
+ font-size: 1.2rem;
312
+ cursor: pointer;
313
+ opacity: 0.8;
314
+ }
315
+
316
+ .close-chat:hover {
317
+ opacity: 1;
318
+ }
319
+
320
+ .chat-messages {
321
+ flex: 1;
322
+ padding: 1rem;
323
+ overflow-y: auto;
324
+ background: #f9f9f9;
325
+ display: flex;
326
+ flex-direction: column;
327
+ gap: 10px;
328
+ }
329
+
330
+ .message {
331
+ max-width: 80%;
332
+ padding: 10px 15px;
333
+ border-radius: 15px;
334
+ font-size: 0.95rem;
335
+ line-height: 1.4;
336
+ position: relative;
337
+ animation: slideUp 0.3s ease;
338
+ }
339
+
340
+ @keyframes slideUp {
341
+ from { transform: translateY(10px); opacity: 0; }
342
+ to { transform: translateY(0); opacity: 1; }
343
+ }
344
+
345
+ .message.bot {
346
+ background: white;
347
+ color: var(--text-dark);
348
+ border-bottom-left-radius: 2px;
349
+ align-self: flex-start;
350
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
351
+ }
352
+
353
+ .message.user {
354
+ background: var(--primary-color);
355
+ color: white;
356
+ border-bottom-right-radius: 2px;
357
+ align-self: flex-end;
358
+ box-shadow: 0 2px 5px rgba(108, 99, 255, 0.2);
359
+ }
360
+
361
+ .chat-input-area {
362
+ padding: 1rem;
363
+ background: white;
364
+ border-top: 1px solid #eee;
365
+ display: flex;
366
+ gap: 10px;
367
+ }
368
+
369
+ .chat-input-area input {
370
+ flex: 1;
371
+ padding: 10px 15px;
372
+ border: 2px solid #eee;
373
+ border-radius: 25px;
374
+ outline: none;
375
+ font-family: 'Nunito', sans-serif;
376
+ transition: border-color 0.2s;
377
+ }
378
+
379
+ .chat-input-area input:focus {
380
+ border-color: var(--primary-color);
381
+ }
382
+
383
+ .send-btn {
384
+ background: var(--primary-color);
385
+ color: white;
386
+ border: none;
387
+ width: 40px;
388
+ height: 40px;
389
+ border-radius: 50%;
390
+ cursor: pointer;
391
+ display: flex;
392
+ justify-content: center;
393
+ align-items: center;
394
+ transition: background 0.2s;
395
+ }
396
+
397
+ .send-btn:hover {
398
+ background: var(--secondary-color);
399
+ }
400
+
401
+ /* Animations */
402
+ @keyframes popIn {
403
+ 0% { transform: scale(0); opacity: 0; }
404
+ 60% { transform: scale(1.2); opacity: 1; }
405
+ 100% { transform: scale(1); opacity: 1; }
406
+ }
407
+
408
+ @keyframes shake {
409
+ 0%, 100% { transform: translateX(0); }
410
+ 25% { transform: translateX(-10px) rotate(-5deg); color: var(--secondary-color); }
411
+ 75% { transform: translateX(10px) rotate(5deg); color: var(--secondary-color); }
412
+ }
413
+
414
+ @keyframes float {
415
+ 0%, 100% { transform: translateY(0px); }
416
+ 50% { transform: translateY(-10px); }
417
+ }
418
+
419
+ .anim-pop { animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; }
420
+ .anim-shake { animation: shake 0.4s ease-in-out; }
421
+ .anim-float { animation: float 3s ease-in-out infinite; }
422
+
423
+ /* Confetti Canvas */
424
+ #confetti-canvas {
425
+ position: absolute;
426
+ top: 0;
427
+ left: 0;
428
+ width: 100%;
429
+ height: 100%;
430
+ pointer-events: none;
431
+ z-index: 50;
432
+ }
433
+
434
+ /* Responsive */
435
+ @media (max-width: 768px) {
436
+ .letter-display { font-size: 7rem; }
437
+ .game-board { width: 95%; padding: 1.5rem; }
438
+ .image-container { width: 140px; height: 140px; }
439
+ }
440
+
441
+ @media (max-width: 480px) {
442
+ .letter-display { font-size: 5rem; }
443
+ .header-actions .anycoder-link span { display: none; }
444
+ .header-actions .anycoder-link { padding: 0.5rem; }
445
+ }
446
+ </style>
447
  </head>
448
+
449
  <body>
450
 
451
+ <header>
452
+ <div class="logo">
453
+ <i class="fa-solid fa-shapes"></i>
454
+ <span>AlphaPop</span>
455
+ </div>
456
+ <div class="header-actions">
457
+ <button class="chat-trigger" onclick="toggleChat()" title="Talk to me">
458
+ <i class="fa-solid fa-comment-dots"></i>
459
+ </button>
460
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
461
+ <span>Built with anycoder</span>
462
+ </a>
463
+ </div>
464
+ </header>
465
+
466
+ <main>
467
+ <canvas id="confetti-canvas"></canvas>
468
+
469
+ <div class="game-board" id="gameBoard">
470
+ <!-- Progress -->
471
+ <div class="progress-container">
472
+ <div class="progress-bar" id="progressBar"></div>
473
+ </div>
474
+
475
+ <!-- Game Area -->
476
+ <div id="gameArea" style="display: none; flex-direction: column; align-items: center; width: 100%;">
477
+ <div class="letter-display anim-pop" id="letterDisplay">A</div>
478
+ <div class="image-container" id="imageContainer">
479
+ <img id="letterImage" src="" alt="Letter Image">
480
  </div>
481
+ <div class="status-text" id="statusText">Press the 'A' key!</div>
482
+ <div class="hint-text"><i class="fa-solid fa-volume-high"></i> Sound On</div>
483
+ </div>
484
+
485
+ <!-- Start Screen -->
486
+ <div id="startScreen" style="text-align: center;">
487
+ <div class="logo" style="justify-content: center; font-size: 3rem; margin-bottom: 1rem; color: var(--primary-color);">
488
+ <i class="fa-solid fa-keyboard"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  </div>
490
+ <h2 style="margin-bottom: 1rem; font-size: 1.8rem;">Ready to type?</h2>
491
+ <p style="margin-bottom: 2rem; color: #636e72;">Press the matching key to hear the sound and see the picture!</p>
492
+ <button class="btn" onclick="startGame()">Start Game <i class="fa-solid fa-play"></i></button>
493
+ <div style="margin-top: 10px;">
494
+ <button class="btn" style="background: transparent; color: var(--primary-color); box-shadow: none; border: 2px solid var(--primary-color); font-size: 1rem; padding: 0.8rem 1.5rem;" onclick="toggleChat()">
495
+ <i class="fa-solid fa-comments"></i> Talk to Me First
496
+ </button>
497
+ </div>
498
+ </div>
499
+
500
+ <!-- End Screen -->
501
+ <div id="endScreen" style="display: none; text-align: center;">
502
+ <i class="fa-solid fa-trophy" style="font-size: 4rem; color: #f1c40f; margin-bottom: 1rem; animation: float 2s infinite;"></i>
503
+ <h2 style="margin-bottom: 1rem; font-size: 2rem;">Great Job!</h2>
504
+ <p style="margin-bottom: 2rem; color: #636e72;">You completed the alphabet.</p>
505
+ <button class="btn" onclick="startGame()">Play Again <i class="fa-solid fa-rotate-right"></i></button>
506
+ </div>
507
+ </div>
508
+ </main>
509
+
510
+ <!-- Chat Interface Overlay -->
511
+ <div class="chat-overlay" id="chatOverlay">
512
+ <div class="chat-window">
513
+ <div class="chat-header">
514
+ <h3><i class="fa-solid fa-robot"></i> AlphaBot</h3>
515
+ <button class="close-chat" onclick="toggleChat()"><i class="fa-solid fa-xmark"></i></button>
516
+ </div>
517
+ <div class="chat-messages" id="chatMessages">
518
+ <div class="message bot">Hello! I'm AlphaBot. I can talk to you! Try saying "Hello" or ask me about the game.</div>
519
+ </div>
520
+ <div class="chat-input-area">
521
+ <input type="text" id="chatInput" placeholder="Type a message..." onkeypress="handleChatEnter(event)">
522
+ <button class="send-btn" onclick="sendMessage()"><i class="fa-solid fa-paper-plane"></i></button>
523
+ </div>
524
+ </div>
525
+ </div>
526
+
527
+ <script>
528
+ // --- Game Configuration & State ---
529
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('');
530
+ let currentIndex = 0;
531
+ let isGameActive = false;
532
+ let isChatOpen = false;
533
+
534
+ // DOM Elements
535
+ const startScreen = document.getElementById('startScreen');
536
+ const endScreen = document.getElementById('endScreen');
537
+ const gameArea = document.getElementById('gameArea');
538
+ const gameBoard = document.getElementById('gameBoard');
539
+ const letterDisplay = document.getElementById('letterDisplay');
540
+ const statusText = document.getElementById('statusText');
541
+ const imageContainer = document.getElementById('imageContainer');
542
+ const letterImage = document.getElementById('letterImage');
543
+ const progressBar = document.getElementById('progressBar');
544
+
545
+ // Chat Elements
546
+ const chatOverlay = document.getElementById('chatOverlay');
547
+ const chatMessages = document.getElementById('chatMessages');
548
+ const chatInput = document.getElementById('chatInput');
549
+
550
+ // --- Chat Logic ---
551
+ function toggleChat() {
552
+ isChatOpen = !isChatOpen;
553
+ if (isChatOpen) {
554
+ chatOverlay.classList.add('active');
555
+ gameBoard.classList.add('blurred');
556
+ chatInput.focus();
557
+ } else {
558
+ chatOverlay.classList.remove('active');
559
+ gameBoard.classList.remove('blurred');
560
+ // Return focus to game if active
561
+ if(isGameActive) gameBoard.focus();
562
+ }
563
+ }
564
+
565
+ function handleChatEnter(e) {
566
+ if (e.key === 'Enter') sendMessage();
567
+ }
568
+
569
+ function sendMessage() {
570
+ const text = chatInput.value.trim();
571
+ if (!text) return;
572
+
573
+ // Add User Message
574
+ addMessage(text, 'user');
575
+ chatInput.value = '';
576
+
577
+ // Generate Bot Response
578
+ const reply = getBotResponse(text);
579
+
580
+ // Simulate typing delay
581
+ setTimeout(() => {
582
+ addMessage(reply, 'bot');
583
+ speakText(reply); // Speak the reply
584
+ }, 600);
585
+ }
586
+
587
+ function addMessage(text, sender) {
588
+ const div = document.createElement('div');
589
+ div.className = `message ${sender}`;
590
+ div.textContent = text;
591
+ chatMessages.appendChild(div);
592
+ chatMessages.scrollTop = chatMessages.scrollHeight;
593
+ }
594
+
595
+ function getBotResponse(input) {
596
+ const lower = input.toLowerCase();
597
+
598
+ if (lower.includes('hello') || lower.includes('hi')) {
599
+ return "Hi there! Are you ready to learn the alphabet?";
600
+ } else if (lower.includes('play') || lower.includes('game')) {
601
+ return "Close this chat and click 'Start Game' to begin! You can also type letters like 'A' or 'B' here to hear them.";
602
+ } else if (lower.includes('name')) {
603
+ return "I am AlphaBot, your friendly coding assistant.";
604
+ } else if (lower.includes('help')) {
605
+ return "I can help you practice letters. Just type a letter, or close the chat to play the typing game.";
606
+ } else if (/^[a-z]$/.test(lower)) {
607
+ // If user types a single letter
608
+ return `You typed ${input.toUpperCase()}. Good job!`;
609
+ } else {
610
+ const randomReplies = [
611
+ "That's interesting!",
612
+ "Tell me more!",
613
+ "I'm listening.",
614
+ "Can you teach me a new word?",
615
+ "Let's get back to learning soon!"
616
+ ];
617
+ return randomReplies[Math.floor(Math.random() * randomReplies.length)];
618
+ }
619
+ }
620
+
621
+ // --- Speech Synthesis (Shared) ---
622
+ function speakText(text) {
623
+ if ('speechSynthesis' in window) {
624
+ window.speechSynthesis.cancel();
625
+ const utterance = new SpeechSynthesisUtterance(text);
626
+ utterance.lang = 'en-US';
627
+ utterance.rate = 1.0;
628
+ utterance.pitch = 1.0;
629
+ window.speechSynthesis.speak(utterance);
630
+ }
631
+ }
632
+
633
+ function speakLetter(letter) {
634
+ speakText(letter); // Reuse the main speak function
635
+ }
636
+
637
+ // --- Game Logic ---
638
+ function startGame() {
639
+ currentIndex = 0;
640
+ isGameActive = true;
641
+
642
+ startScreen.style.display = 'none';
643
+ endScreen.style.display = 'none';
644
+ gameArea.style.display = 'flex';
645
+
646
+ // Close chat if open
647
+ if(isChatOpen) toggleChat();
648
+
649
+ updateLevel();
650
+ }
651
 
652
+ function updateLevel() {
653
+ if (currentIndex >= alphabet.length) {
654
+ endGame();
655
+ return;
 
 
 
 
 
 
 
656
  }
657
 
658
+ const currentLetter = alphabet[currentIndex];
659
+
660
+ // Reset Animations
661
+ letterDisplay.classList.remove('anim-pop', 'anim-shake');
662
+ void letterDisplay.offsetWidth;
663
+ letterDisplay.classList.add('anim-pop');
664
+
665
+ letterDisplay.textContent = currentLetter;
666
+ statusText.textContent = `Press the '${currentLetter}' key!`;
667
+ imageContainer.classList.remove('visible');
668
+
669
+ const progress = (currentIndex / alphabet.length) * 100;
670
+ progressBar.style.width = `${progress}%`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
 
672
+ speakLetter(currentLetter);
673
+ }
674
 
675
+ function handleInput(e) {
676
+ // If chat is open, don't process game keys
677
+ if (isChatOpen) return;
678
 
679
+ if (!isGameActive) return;
 
680
 
681
+ const key = e.key.toUpperCase();
682
+ const targetLetter = alphabet[currentIndex];
 
 
 
 
 
 
683
 
684
+ if (!/^[A-Z]$/.test(key)) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
 
686
+ if (key === targetLetter) {
687
+ onCorrectPress(targetLetter);
688
+ } else {
689
+ onWrongPress();
 
 
 
 
 
 
 
 
690
  }
691
+ }
692
 
693
+ function onCorrectPress(letter) {
694
+ speakLetter(letter);
695
+
696
+ // Random Image Placeholder
697
+ const randomId = Math.floor(Math.random() * 1000);
698
+ const imgUrl = `https://picsum.photos/seed/${letter}${randomId}/300/300`;
699
+ letterImage.src = imgUrl;
700
+
701
+ letterImage.onload = () => {
702
+ imageContainer.classList.add('visible');
703
+ };
704
+ if(letterImage.complete) imageContainer.classList.add('visible');
705
 
706
+ letterDisplay.style.color = "var(--accent-color)";
707
+ statusText.textContent = "Great!";
708
+ statusText.style.color = "var(--accent-color)";
709
 
710
+ fireConfetti(0.5);
 
 
 
711
 
712
+ setTimeout(() => {
713
+ currentIndex++;
714
+ letterDisplay.style.color = "var(--primary-color)";
715
+ statusText.style.color = "#636e72";
716
+ updateLevel();
717
+ }, 1500);
718
+ }
719
 
720
+ function onWrongPress() {
721
+ letterDisplay.classList.remove('anim-shake');
722
+ void letterDisplay.offsetWidth;
723
+ letterDisplay.classList.add('anim-shake');
724
+
725
+ statusText.textContent = "Oops, try again!";
726
+ statusText.style.color = "var(--secondary-color)";
727
+ speakLetter(alphabet[currentIndex]);
728
+ }
729
+
730
+ function endGame() {
731
+ isGameActive = false;
732
+ gameArea.style.display = 'none';
733
+ endScreen.style.display = 'block';
734
+ progressBar.style.width = '100%';
735
+ fireConfetti(1.0);
736
+ speakText("Great job! You finished the game!");
737
+ }
738
+
739
+ // Event Listeners
740
+ window.addEventListener('keydown', handleInput);
741
+
742
+ // --- Confetti Logic ---
743
+ const canvas = document.getElementById('confetti-canvas');
744
+ const ctx = canvas.getContext('2d');
745
+ let particles = [];
746
+
747
+ function resizeCanvas() {
748
+ canvas.width = window.innerWidth;
749
+ canvas.height = window.innerHeight;
750
+ }
751
+ window.addEventListener('resize', resizeCanvas);
752
+ resizeCanvas();
753
+
754
+ function fireConfetti(intensity = 1.0) {
755
+ const particleCount = 100 * intensity;
756
+ const colors = ['#FF6584', '#6C63FF', '#43D9AD', '#FFD93D', '#FF8C00'];
757
+
758
+ for (let i = 0; i < particleCount; i++) {
759
+ particles.push({
760
+ x: canvas.width / 2,
761
+ y: canvas.height / 2 + 50,
762
+ vx: (Math.random() - 0.5) * 20 * intensity,
763
+ vy: (Math.random() - 1) * 20 * intensity,
764
+ size: Math.random() * 8 + 4,
765
+ color: colors[Math.floor(Math.random() * colors.length)],
766
+ life: 100,
767
+ gravity: 0.5
768
+ });
769
+ }
770
+ if(particles.length <= particleCount) requestAnimationFrame(updateConfetti);
771
+ }
772
+
773
+ function updateConfetti() {
774
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
775
+
776
+ for (let i = 0; i < particles.length; i++) {
777
+ let p = particles[i];
778
+ p.x += p.vx;
779
+ p.y += p.vy;
780
+ p.vy += p.gravity;
781
+ p.life--;
782
 
783
+ ctx.fillStyle = p.color;
784
+ ctx.beginPath();
785
+ ctx.rect(p.x, p.y, p.size, p.size);
786
+ ctx.fill();
 
 
 
 
 
 
 
 
 
787
  }
788
+
789
+ particles = particles.filter(p => p.life > 0);
790
+
791
+ if (particles.length > 0) {
792
+ requestAnimationFrame(updateConfetti);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
793
  }
794
+ }
795
+ </script>
796
  </body>
797
+
798
  </html>