pijou commited on
Commit
0ce141e
·
verified ·
1 Parent(s): 33133ef

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +786 -88
index.html CHANGED
@@ -3,39 +3,67 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Neon Snake Game</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
 
8
  <style>
9
  @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
10
 
11
  body {
12
  font-family: 'Press Start 2P', cursive;
13
- background-color: #0f172a;
14
  overflow: hidden;
 
15
  }
16
 
17
  #gameCanvas {
18
- box-shadow: 0 0 20px #3b82f6;
 
 
19
  border-radius: 8px;
 
20
  }
21
 
22
- .neon-text {
23
- text-shadow: 0 0 5px #3b82f6, 0 0 10px #3b82f6, 0 0 15px #3b82f6;
 
 
 
 
 
 
24
  }
25
 
26
  .neon-button {
27
  transition: all 0.3s;
28
- box-shadow: 0 0 10px #3b82f6;
 
 
29
  }
30
 
31
  .neon-button:hover {
32
- box-shadow: 0 0 20px #3b82f6, 0 0 30px #3b82f6;
33
  transform: translateY(-2px);
34
  }
35
 
36
- .snake-cell {
37
- border-radius: 3px;
38
- transition: all 0.1s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
 
41
  .pulse {
@@ -43,50 +71,216 @@
43
  }
44
 
45
  @keyframes pulse {
46
- 0% { opacity: 0.7; }
47
- 50% { opacity: 1; }
48
- 100% { opacity: 0.7; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
  </style>
51
  </head>
52
  <body class="min-h-screen flex flex-col items-center justify-center p-4">
53
  <div class="text-center mb-8">
54
- <h1 class="text-4xl md:text-5xl font-bold text-blue-400 neon-text mb-4">NEON SNAKE</h1>
55
- <div class="flex justify-center gap-8 text-blue-300 mb-6">
56
- <div>
57
- <p class="text-sm">SCORE</p>
58
- <p id="score" class="text-2xl">0</p>
59
  </div>
60
- <div>
61
- <p class="text-sm">HIGH SCORE</p>
62
- <p id="highScore" class="text-2xl">0</p>
63
  </div>
64
  </div>
65
  </div>
66
 
67
  <div class="relative">
68
- <canvas id="gameCanvas" width="400" height="400" class="bg-gray-900 border-2 border-blue-500"></canvas>
69
 
70
- <div id="gameOver" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 rounded">
71
- <h2 class="text-red-500 text-3xl mb-6 neon-text">GAME OVER!</h2>
72
- <p class="text-blue-300 mb-8">Your score: <span id="finalScore" class="text-white">0</span></p>
73
- <button id="restartButton" class="px-6 py-3 bg-blue-600 text-white rounded-full neon-button font-bold">
74
- PLAY AGAIN
 
75
  </button>
76
  </div>
77
 
78
- <div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 rounded">
79
- <h2 class="text-blue-400 text-3xl mb-6 neon-text pulse">NEON SNAKE</h2>
80
- <p class="text-blue-300 mb-8 text-center px-4">Use arrow keys or swipe to control the snake</p>
81
- <button id="startButton" class="px-6 py-3 bg-blue-600 text-white rounded-full neon-button font-bold">
82
- START GAME
83
  </button>
84
  </div>
85
  </div>
86
 
87
- <div class="mt-8 text-blue-300 text-sm text-center">
88
- <p>Controls: Arrow Keys or Swipe</p>
89
- <p class="mt-2">Eat the food (●) to grow longer!</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  </div>
91
 
92
  <script>
@@ -96,10 +290,12 @@
96
  const scoreElement = document.getElementById('score');
97
  const highScoreElement = document.getElementById('highScore');
98
  const finalScoreElement = document.getElementById('finalScore');
 
99
  const gameOverScreen = document.getElementById('gameOver');
100
  const startScreen = document.getElementById('startScreen');
101
  const startButton = document.getElementById('startButton');
102
  const restartButton = document.getElementById('restartButton');
 
103
 
104
  // Game settings
105
  const gridSize = 20;
@@ -109,65 +305,286 @@
109
  // Game state
110
  let snake = [{x: 10, y: 10}];
111
  let food = {x: 5, y: 5};
 
112
  let direction = {x: 0, y: 0};
113
  let nextDirection = {x: 0, y: 0};
114
  let score = 0;
115
  let highScore = localStorage.getItem('snakeHighScore') || 0;
116
  let gameRunning = false;
117
  let gameLoop;
118
- let touchStartX = 0;
119
- let touchStartY = 0;
 
 
 
 
 
120
 
121
  highScoreElement.textContent = highScore;
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  // Draw functions
124
  function drawSnake() {
125
  snake.forEach((segment, index) => {
126
- // Head is a different color
127
- if (index === 0) {
128
- ctx.fillStyle = '#3b82f6';
 
 
 
 
 
 
 
129
  } else {
130
- // Body segments get progressively darker
131
- const intensity = 200 - Math.min(190, index * 5);
132
- ctx.fillStyle = `rgb(59, 130, ${intensity})`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
 
135
- ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- // Add a subtle glow effect
138
  if (index === 0) {
139
- ctx.shadowColor = '#3b82f6';
140
- ctx.shadowBlur = 10;
141
- ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize);
142
  ctx.shadowBlur = 0;
143
  }
 
 
 
 
 
144
  });
145
  }
146
 
147
  function drawFood() {
148
- ctx.fillStyle = '#ef4444';
149
- ctx.beginPath();
150
- const centerX = food.x * gridSize + gridSize / 2;
151
- const centerY = food.y * gridSize + gridSize / 2;
152
  const radius = gridSize / 2;
153
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
154
- ctx.fill();
155
 
156
- // Add glow effect
157
  ctx.shadowColor = '#ef4444';
158
  ctx.shadowBlur = 15;
 
 
 
 
 
 
 
 
 
159
  ctx.fill();
160
- ctx.shadowBlur = 0;
161
 
162
- // Add inner circle for depth
163
- ctx.fillStyle = '#fca5a5';
164
  ctx.beginPath();
165
- ctx.arc(centerX, centerY, radius / 2, 0, Math.PI * 2);
166
  ctx.fill();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
 
169
  function drawGrid() {
170
- ctx.strokeStyle = '#1e293b';
171
  ctx.lineWidth = 0.5;
172
 
173
  for (let i = 0; i < tileCount; i++) {
@@ -185,6 +602,28 @@
185
  }
186
  }
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  // Game logic
189
  function update() {
190
  // Update direction from the next direction buffer
@@ -193,17 +632,39 @@
193
  // Move snake
194
  const head = {x: snake[0].x + direction.x, y: snake[0].y + direction.y};
195
 
196
- // Check wall collision
197
- if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
 
 
 
 
 
 
198
  gameOver();
199
  return;
200
  }
201
 
202
- // Check self collision
203
- for (let i = 1; i < snake.length; i++) {
204
- if (head.x === snake[i].x && head.y === snake[i].y) {
205
- gameOver();
206
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  }
208
  }
209
 
@@ -212,21 +673,92 @@
212
 
213
  // Check food collision
214
  if (head.x === food.x && head.y === food.y) {
 
 
 
 
 
 
 
 
215
  // Increase score
216
  score += 10;
217
  scoreElement.textContent = score;
218
 
219
- // Generate new food (not on snake)
220
  generateFood();
221
 
222
  // Increase speed slightly every 50 points
223
  if (score % 50 === 0 && speed < 15) {
224
  speed += 0.5;
225
  }
 
 
 
 
 
226
  } else {
227
  // Remove tail if no food eaten
228
  snake.pop();
229
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
 
232
  function generateFood() {
@@ -238,19 +770,71 @@
238
  y: Math.floor(Math.random() * tileCount)
239
  };
240
 
241
- // Check if food is on snake
242
  validPosition = true;
 
243
  for (let segment of snake) {
244
  if (segment.x === food.x && segment.y === food.y) {
245
  validPosition = false;
246
  break;
247
  }
248
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
250
  }
251
 
252
  function gameOver() {
253
- clearInterval(gameLoop);
254
  gameRunning = false;
255
 
256
  // Update high score
@@ -261,6 +845,7 @@
261
  }
262
 
263
  finalScoreElement.textContent = score;
 
264
  gameOverScreen.classList.remove('hidden');
265
  }
266
 
@@ -271,22 +856,45 @@
271
  score = 0;
272
  scoreElement.textContent = score;
273
  speed = 7;
 
 
 
 
 
 
 
274
  generateFood();
275
  gameOverScreen.classList.add('hidden');
276
  }
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  function startGame() {
279
  resetGame();
280
  startScreen.classList.add('hidden');
281
  gameRunning = true;
282
-
283
- gameLoop = setInterval(() => {
284
- ctx.clearRect(0, 0, canvas.width, canvas.height);
285
- drawGrid();
286
- drawFood();
287
- drawSnake();
288
- update();
289
- }, 1000 / speed);
290
  }
291
 
292
  // Event listeners
@@ -299,25 +907,89 @@
299
  // Prevent reverse direction
300
  switch (e.key) {
301
  case 'ArrowUp':
 
 
302
  if (direction.y === 0) nextDirection = {x: 0, y: -1};
303
  break;
304
  case 'ArrowDown':
 
 
305
  if (direction.y === 0) nextDirection = {x: 0, y: 1};
306
  break;
307
  case 'ArrowLeft':
 
 
308
  if (direction.x === 0) nextDirection = {x: -1, y: 0};
309
  break;
310
  case 'ArrowRight':
 
 
311
  if (direction.x === 0) nextDirection = {x: 1, y: 0};
312
  break;
313
  }
314
  });
315
 
316
- // Touch controls for mobile
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  canvas.addEventListener('touchstart', (e) => {
318
  touchStartX = e.touches[0].clientX;
319
  touchStartY = e.touches[0].clientY;
320
- }, false);
 
321
 
322
  canvas.addEventListener('touchmove', (e) => {
323
  if (!touchStartX || !touchStartY || !gameRunning) return;
@@ -331,34 +1003,60 @@
331
  // Determine swipe direction
332
  if (Math.abs(diffX) > Math.abs(diffY)) {
333
  // Horizontal swipe
334
- if (diffX > 0 && direction.x === 0) {
335
  nextDirection = {x: -1, y: 0}; // Left swipe
336
- } else if (diffX < 0 && direction.x === 0) {
 
 
337
  nextDirection = {x: 1, y: 0}; // Right swipe
 
 
338
  }
339
  } else {
340
  // Vertical swipe
341
- if (diffY > 0 && direction.y === 0) {
342
  nextDirection = {x: 0, y: -1}; // Up swipe
343
- } else if (diffY < 0 && direction.y === 0) {
 
 
344
  nextDirection = {x: 0, y: 1}; // Down swipe
 
 
345
  }
346
  }
347
 
348
- // Reset touch coordinates
349
- touchStartX = 0;
350
- touchStartY = 0;
351
  e.preventDefault();
352
- }, false);
353
 
354
  // Button event listeners
355
  startButton.addEventListener('click', startGame);
356
  restartButton.addEventListener('click', startGame);
357
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  // Initial draw
 
359
  drawGrid();
360
  drawFood();
361
  drawSnake();
 
 
 
 
 
 
 
362
  });
363
  </script>
364
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=pijou/snake" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Rainbow Neon Snake</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
10
  @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
11
 
12
  body {
13
  font-family: 'Press Start 2P', cursive;
14
+ background: linear-gradient(135deg, #0f172a, #1e293b);
15
  overflow: hidden;
16
+ touch-action: none;
17
  }
18
 
19
  #gameCanvas {
20
+ box-shadow: 0 0 30px rgba(99, 102, 241, 0.8),
21
+ 0 0 60px rgba(139, 92, 246, 0.6),
22
+ 0 0 90px rgba(217, 70, 239, 0.4);
23
  border-radius: 8px;
24
+ border: 2px solid #6366f1;
25
  }
26
 
27
+ .rainbow-text {
28
+ background: linear-gradient(90deg,
29
+ #ef4444, #f97316, #eab308, #22c55e,
30
+ #3b82f6, #6366f1, #8b5cf6, #d946ef);
31
+ -webkit-background-clip: text;
32
+ background-clip: text;
33
+ color: transparent;
34
+ text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
35
  }
36
 
37
  .neon-button {
38
  transition: all 0.3s;
39
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.7);
40
+ position: relative;
41
+ overflow: hidden;
42
  }
43
 
44
  .neon-button:hover {
45
+ box-shadow: 0 0 25px rgba(139, 92, 246, 0.8);
46
  transform: translateY(-2px);
47
  }
48
 
49
+ .neon-button::before {
50
+ content: '';
51
+ position: absolute;
52
+ top: -50%;
53
+ left: -50%;
54
+ width: 200%;
55
+ height: 200%;
56
+ background: linear-gradient(
57
+ to bottom right,
58
+ rgba(255, 255, 255, 0.3) 0%,
59
+ rgba(255, 255, 255, 0) 60%
60
+ );
61
+ transform: rotate(30deg);
62
+ transition: all 0.3s;
63
+ }
64
+
65
+ .neon-button:hover::before {
66
+ left: 100%;
67
  }
68
 
69
  .pulse {
 
71
  }
72
 
73
  @keyframes pulse {
74
+ 0% { transform: scale(0.98); opacity: 0.9; }
75
+ 50% { transform: scale(1.02); opacity: 1; }
76
+ 100% { transform: scale(0.98); opacity: 0.9; }
77
+ }
78
+
79
+ .glow {
80
+ animation: glow 2s infinite alternate;
81
+ }
82
+
83
+ @keyframes glow {
84
+ from {
85
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.7),
86
+ 0 0 30px rgba(139, 92, 246, 0.5);
87
+ }
88
+ to {
89
+ box-shadow: 0 0 25px rgba(99, 102, 241, 0.9),
90
+ 0 0 50px rgba(139, 92, 246, 0.7),
91
+ 0 0 75px rgba(217, 70, 239, 0.5);
92
+ }
93
+ }
94
+
95
+ .particle {
96
+ position: absolute;
97
+ border-radius: 50%;
98
+ pointer-events: none;
99
+ z-index: 10;
100
+ }
101
+
102
+ .controls {
103
+ touch-action: none;
104
+ }
105
+
106
+ .control-btn {
107
+ width: 70px;
108
+ height: 70px;
109
+ background: rgba(30, 41, 59, 0.7);
110
+ border: 2px solid;
111
+ border-radius: 50%;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ color: white;
116
+ font-size: 28px;
117
+ user-select: none;
118
+ touch-action: none;
119
+ transition: all 0.2s;
120
+ position: relative;
121
+ overflow: hidden;
122
+ }
123
+
124
+ .control-btn::before {
125
+ content: '';
126
+ position: absolute;
127
+ top: -50%;
128
+ left: -50%;
129
+ width: 200%;
130
+ height: 200%;
131
+ background: linear-gradient(
132
+ to bottom right,
133
+ rgba(255, 255, 255, 0.2) 0%,
134
+ rgba(255, 255, 255, 0) 60%
135
+ );
136
+ transform: rotate(30deg);
137
+ }
138
+
139
+ .control-btn:active {
140
+ transform: scale(0.95);
141
+ }
142
+
143
+ #upBtn {
144
+ border-color: #22c55e;
145
+ box-shadow: 0 0 15px rgba(34, 197, 94, 0.5);
146
+ }
147
+
148
+ #downBtn {
149
+ border-color: #3b82f6;
150
+ box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
151
+ }
152
+
153
+ #leftBtn {
154
+ border-color: #f97316;
155
+ box-shadow: 0 0 15px rgba(249, 115, 22, 0.5);
156
+ }
157
+
158
+ #rightBtn {
159
+ border-color: #d946ef;
160
+ box-shadow: 0 0 15px rgba(217, 70, 239, 0.5);
161
+ }
162
+
163
+ .score-box {
164
+ background: rgba(30, 41, 59, 0.7);
165
+ border: 2px solid #6366f1;
166
+ border-radius: 10px;
167
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.5);
168
+ padding: 10px 20px;
169
+ position: relative;
170
+ overflow: hidden;
171
+ }
172
+
173
+ .score-box::before {
174
+ content: '';
175
+ position: absolute;
176
+ top: 0;
177
+ left: 0;
178
+ right: 0;
179
+ height: 3px;
180
+ background: linear-gradient(90deg,
181
+ #ef4444, #f97316, #eab308, #22c55e,
182
+ #3b82f6, #6366f1, #8b5cf6, #d946ef);
183
+ }
184
+
185
+ .trail {
186
+ position: absolute;
187
+ width: 20px;
188
+ height: 20px;
189
+ border-radius: 50%;
190
+ background: rgba(255, 255, 255, 0.4);
191
+ pointer-events: none;
192
+ z-index: 5;
193
+ }
194
+
195
+ .power-up {
196
+ position: absolute;
197
+ width: 20px;
198
+ height: 20px;
199
+ border-radius: 50%;
200
+ background: #eab308;
201
+ box-shadow: 0 0 10px #eab308, 0 0 20px rgba(234, 179, 8, 0.7);
202
+ animation: float 3s ease-in-out infinite;
203
+ z-index: 5;
204
+ }
205
+
206
+ @keyframes float {
207
+ 0%, 100% { transform: translateY(0); }
208
+ 50% { transform: translateY(-10px); }
209
+ }
210
+
211
+ .power-up::after {
212
+ content: '';
213
+ position: absolute;
214
+ top: -5px;
215
+ left: -5px;
216
+ right: -5px;
217
+ bottom: -5px;
218
+ border: 2px solid #eab308;
219
+ border-radius: 50%;
220
+ animation: pulse-ring 2s ease-out infinite;
221
+ opacity: 0;
222
+ }
223
+
224
+ @keyframes pulse-ring {
225
+ 0% { transform: scale(0.8); opacity: 0.8; }
226
+ 100% { transform: scale(1.5); opacity: 0; }
227
  }
228
  </style>
229
  </head>
230
  <body class="min-h-screen flex flex-col items-center justify-center p-4">
231
  <div class="text-center mb-8">
232
+ <h1 class="text-4xl md:text-5xl font-bold rainbow-text mb-4">RAINBOW SNAKE</h1>
233
+ <div class="flex justify-center gap-8 mb-6">
234
+ <div class="score-box">
235
+ <p class="text-sm text-blue-300">SCORE</p>
236
+ <p id="score" class="text-2xl text-white">0</p>
237
  </div>
238
+ <div class="score-box">
239
+ <p class="text-sm text-blue-300">HIGH SCORE</p>
240
+ <p id="highScore" class="text-2xl text-white">0</p>
241
  </div>
242
  </div>
243
  </div>
244
 
245
  <div class="relative">
246
+ <canvas id="gameCanvas" width="400" height="400" class="bg-gray-900"></canvas>
247
 
248
+ <div id="gameOver" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-90 rounded">
249
+ <h2 class="text-red-500 text-3xl mb-6 rainbow-text">GAME OVER!</h2>
250
+ <p class="text-blue-300 mb-4">Your score: <span id="finalScore" class="text-white">0</span></p>
251
+ <p class="text-blue-300 mb-8">High score: <span id="finalHighScore" class="text-white">0</span></p>
252
+ <button id="restartButton" class="px-6 py-3 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-full neon-button font-bold glow">
253
+ <i class="fas fa-redo mr-2"></i> PLAY AGAIN
254
  </button>
255
  </div>
256
 
257
+ <div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-90 rounded">
258
+ <h2 class="rainbow-text text-3xl mb-6 pulse">RAINBOW SNAKE</h2>
259
+ <p class="text-blue-300 mb-8 text-center px-4">Eat the food to grow longer! Collect power-ups for bonuses!</p>
260
+ <button id="startButton" class="px-6 py-3 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-full neon-button font-bold glow">
261
+ <i class="fas fa-play mr-2"></i> START GAME
262
  </button>
263
  </div>
264
  </div>
265
 
266
+ <div class="mt-6 grid grid-cols-3 gap-4 controls" id="mobileControls">
267
+ <div></div>
268
+ <button class="control-btn" id="upBtn"><i class="fas fa-arrow-up"></i></button>
269
+ <div></div>
270
+ <button class="control-btn" id="leftBtn"><i class="fas fa-arrow-left"></i></button>
271
+ <div class="flex items-center justify-center">
272
+ <div class="w-12 h-12 rounded-full bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center text-white">
273
+ <i class="fas fa-gamepad"></i>
274
+ </div>
275
+ </div>
276
+ <button class="control-btn" id="rightBtn"><i class="fas fa-arrow-right"></i></button>
277
+ <div></div>
278
+ <button class="control-btn" id="downBtn"><i class="fas fa-arrow-down"></i></button>
279
+ <div></div>
280
+ </div>
281
+
282
+ <div class="mt-4 text-blue-300 text-sm text-center">
283
+ <p>Controls: Arrow Keys, WASD, or Touch Buttons</p>
284
  </div>
285
 
286
  <script>
 
290
  const scoreElement = document.getElementById('score');
291
  const highScoreElement = document.getElementById('highScore');
292
  const finalScoreElement = document.getElementById('finalScore');
293
+ const finalHighScoreElement = document.getElementById('finalHighScore');
294
  const gameOverScreen = document.getElementById('gameOver');
295
  const startScreen = document.getElementById('startScreen');
296
  const startButton = document.getElementById('startButton');
297
  const restartButton = document.getElementById('restartButton');
298
+ const mobileControls = document.getElementById('mobileControls');
299
 
300
  // Game settings
301
  const gridSize = 20;
 
305
  // Game state
306
  let snake = [{x: 10, y: 10}];
307
  let food = {x: 5, y: 5};
308
+ let powerUps = [];
309
  let direction = {x: 0, y: 0};
310
  let nextDirection = {x: 0, y: 0};
311
  let score = 0;
312
  let highScore = localStorage.getItem('snakeHighScore') || 0;
313
  let gameRunning = false;
314
  let gameLoop;
315
+ let particles = [];
316
+ let trails = [];
317
+ let lastRenderTime = 0;
318
+ let powerUpTimer = 0;
319
+ let invincible = false;
320
+ let rainbowMode = false;
321
+ let rainbowTimer = 0;
322
 
323
  highScoreElement.textContent = highScore;
324
 
325
+ // Color palette
326
+ const colors = [
327
+ '#ef4444', // red
328
+ '#f97316', // orange
329
+ '#eab308', // yellow
330
+ '#22c55e', // green
331
+ '#3b82f6', // blue
332
+ '#6366f1', // indigo
333
+ '#8b5cf6', // violet
334
+ '#d946ef' // pink
335
+ ];
336
+
337
+ // Visual effects
338
+ function createParticles(x, y, color, count = 15) {
339
+ for (let i = 0; i < count; i++) {
340
+ const size = Math.random() * 4 + 2;
341
+ particles.push({
342
+ x: x,
343
+ y: y,
344
+ size: size,
345
+ color: color || colors[Math.floor(Math.random() * colors.length)],
346
+ speedX: Math.random() * 8 - 4,
347
+ speedY: Math.random() * 8 - 4,
348
+ life: 30 + Math.random() * 20,
349
+ shrink: size * 0.05
350
+ });
351
+ }
352
+ }
353
+
354
+ function createTrail(x, y) {
355
+ if (Math.random() > 0.3) return; // Limit trail density
356
+
357
+ trails.push({
358
+ x: x * gridSize + gridSize / 2,
359
+ y: y * gridSize + gridSize / 2,
360
+ size: Math.random() * 10 + 5,
361
+ opacity: 0.7,
362
+ fade: 0.02
363
+ });
364
+ }
365
+
366
+ function updateParticles() {
367
+ for (let i = particles.length - 1; i >= 0; i--) {
368
+ const p = particles[i];
369
+ p.x += p.speedX;
370
+ p.y += p.speedY;
371
+ p.life--;
372
+ p.size -= p.shrink;
373
+
374
+ if (p.life <= 0 || p.size <= 0) {
375
+ particles.splice(i, 1);
376
+ }
377
+ }
378
+
379
+ for (let i = trails.length - 1; i >= 0; i--) {
380
+ const t = trails[i];
381
+ t.opacity -= t.fade;
382
+ if (t.opacity <= 0) {
383
+ trails.splice(i, 1);
384
+ }
385
+ }
386
+ }
387
+
388
+ function drawParticles() {
389
+ // Draw trails first (under everything)
390
+ trails.forEach(t => {
391
+ ctx.globalAlpha = t.opacity;
392
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
393
+ ctx.beginPath();
394
+ ctx.arc(t.x, t.y, t.size, 0, Math.PI * 2);
395
+ ctx.fill();
396
+ });
397
+
398
+ // Then draw particles
399
+ particles.forEach(p => {
400
+ ctx.globalAlpha = p.life / 50;
401
+ ctx.fillStyle = p.color;
402
+ ctx.beginPath();
403
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
404
+ ctx.fill();
405
+ });
406
+
407
+ ctx.globalAlpha = 1;
408
+ }
409
+
410
  // Draw functions
411
  function drawSnake() {
412
  snake.forEach((segment, index) => {
413
+ // Create gradient based on position or rainbow mode
414
+ let gradient;
415
+ const x = segment.x * gridSize;
416
+ const y = segment.y * gridSize;
417
+
418
+ if (rainbowMode) {
419
+ // Rainbow effect for all segments
420
+ gradient = ctx.createLinearGradient(x, y, x + gridSize, y + gridSize);
421
+ gradient.addColorStop(0, colors[index % colors.length]);
422
+ gradient.addColorStop(1, colors[(index + 4) % colors.length]);
423
  } else {
424
+ // Head is a different color
425
+ if (index === 0) {
426
+ gradient = ctx.createRadialGradient(
427
+ x + gridSize / 2,
428
+ y + gridSize / 2,
429
+ 0,
430
+ x + gridSize / 2,
431
+ y + gridSize / 2,
432
+ gridSize
433
+ );
434
+ gradient.addColorStop(0, '#3b82f6');
435
+ gradient.addColorStop(1, 'rgba(59, 130, 246, 0.2)');
436
+ } else {
437
+ // Body segments get progressively darker
438
+ const intensity = 200 - Math.min(190, index * 5);
439
+ gradient = `rgb(59, 130, ${intensity})`;
440
+ }
441
  }
442
 
443
+ ctx.fillStyle = gradient;
444
+
445
+ // Draw rounded rectangle for snake segments
446
+ const radius = gridSize / 4;
447
+
448
+ ctx.beginPath();
449
+ ctx.moveTo(x + radius, y);
450
+ ctx.lineTo(x + gridSize - radius, y);
451
+ ctx.quadraticCurveTo(x + gridSize, y, x + gridSize, y + radius);
452
+ ctx.lineTo(x + gridSize, y + gridSize - radius);
453
+ ctx.quadraticCurveTo(x + gridSize, y + gridSize, x + gridSize - radius, y + gridSize);
454
+ ctx.lineTo(x + radius, y + gridSize);
455
+ ctx.quadraticCurveTo(x, y + gridSize, x, y + gridSize - radius);
456
+ ctx.lineTo(x, y + radius);
457
+ ctx.quadraticCurveTo(x, y, x + radius, y);
458
+ ctx.closePath();
459
+ ctx.fill();
460
 
461
+ // Add glow effect to head
462
  if (index === 0) {
463
+ ctx.shadowColor = rainbowMode ? colors[Math.floor(Math.random() * colors.length)] : '#3b82f6';
464
+ ctx.shadowBlur = 15;
465
+ ctx.fill();
466
  ctx.shadowBlur = 0;
467
  }
468
+
469
+ // Create trail effect
470
+ if (gameRunning && index === 0) {
471
+ createTrail(segment.x, segment.y);
472
+ }
473
  });
474
  }
475
 
476
  function drawFood() {
477
+ const x = food.x * gridSize + gridSize / 2;
478
+ const y = food.y * gridSize + gridSize / 2;
 
 
479
  const radius = gridSize / 2;
 
 
480
 
481
+ // Outer glow
482
  ctx.shadowColor = '#ef4444';
483
  ctx.shadowBlur = 15;
484
+
485
+ // Main food circle with gradient
486
+ const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
487
+ gradient.addColorStop(0, '#ef4444');
488
+ gradient.addColorStop(1, '#7f1d1d');
489
+ ctx.fillStyle = gradient;
490
+
491
+ ctx.beginPath();
492
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
493
  ctx.fill();
 
494
 
495
+ // Inner highlight
496
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
497
  ctx.beginPath();
498
+ ctx.arc(x - radius/3, y - radius/3, radius/4, 0, Math.PI * 2);
499
  ctx.fill();
500
+
501
+ // Spikes
502
+ const spikeCount = 8;
503
+ for (let i = 0; i < spikeCount; i++) {
504
+ const angle = (i / spikeCount) * Math.PI * 2;
505
+ const spikeLength = radius * 0.6;
506
+ const spikeWidth = radius * 0.2;
507
+
508
+ ctx.fillStyle = '#ef4444';
509
+ ctx.beginPath();
510
+ ctx.moveTo(
511
+ x + Math.cos(angle) * radius,
512
+ y + Math.sin(angle) * radius
513
+ );
514
+ ctx.lineTo(
515
+ x + Math.cos(angle) * (radius + spikeLength),
516
+ y + Math.sin(angle) * (radius + spikeLength)
517
+ );
518
+ ctx.lineTo(
519
+ x + Math.cos(angle + 0.2) * (radius + spikeWidth),
520
+ y + Math.sin(angle + 0.2) * (radius + spikeWidth)
521
+ );
522
+ ctx.closePath();
523
+ ctx.fill();
524
+ }
525
+
526
+ // Reset shadow
527
+ ctx.shadowBlur = 0;
528
+ }
529
+
530
+ function drawPowerUps() {
531
+ powerUps.forEach(pu => {
532
+ const x = pu.x * gridSize + gridSize / 2;
533
+ const y = pu.y * gridSize + gridSize / 2;
534
+ const radius = gridSize / 2;
535
+
536
+ // Glowing effect
537
+ ctx.shadowColor = '#eab308';
538
+ ctx.shadowBlur = 15;
539
+
540
+ // Main circle
541
+ const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
542
+ gradient.addColorStop(0, '#eab308');
543
+ gradient.addColorStop(1, '#713f12');
544
+ ctx.fillStyle = gradient;
545
+
546
+ ctx.beginPath();
547
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
548
+ ctx.fill();
549
+
550
+ // Star shape
551
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
552
+ ctx.beginPath();
553
+ drawStar(x, y, 5, radius * 0.5, radius * 0.2);
554
+ ctx.fill();
555
+
556
+ // Reset shadow
557
+ ctx.shadowBlur = 0;
558
+ });
559
+ }
560
+
561
+ function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
562
+ let rot = Math.PI / 2 * 3;
563
+ let x = cx;
564
+ let y = cy;
565
+ let step = Math.PI / spikes;
566
+
567
+ ctx.beginPath();
568
+ ctx.moveTo(cx, cy - outerRadius);
569
+
570
+ for (let i = 0; i < spikes; i++) {
571
+ x = cx + Math.cos(rot) * outerRadius;
572
+ y = cy + Math.sin(rot) * outerRadius;
573
+ ctx.lineTo(x, y);
574
+ rot += step;
575
+
576
+ x = cx + Math.cos(rot) * innerRadius;
577
+ y = cy + Math.sin(rot) * innerRadius;
578
+ ctx.lineTo(x, y);
579
+ rot += step;
580
+ }
581
+
582
+ ctx.lineTo(cx, cy - outerRadius);
583
+ ctx.closePath();
584
  }
585
 
586
  function drawGrid() {
587
+ ctx.strokeStyle = 'rgba(30, 41, 59, 0.5)';
588
  ctx.lineWidth = 0.5;
589
 
590
  for (let i = 0; i < tileCount; i++) {
 
602
  }
603
  }
604
 
605
+ function drawBackground() {
606
+ // Gradient background
607
+ const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
608
+ gradient.addColorStop(0, '#0f172a');
609
+ gradient.addColorStop(1, '#1e293b');
610
+ ctx.fillStyle = gradient;
611
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
612
+
613
+ // Add subtle star effect
614
+ if (Math.random() < 0.02) {
615
+ const x = Math.random() * canvas.width;
616
+ const y = Math.random() * canvas.height;
617
+ const size = Math.random() * 2 + 1;
618
+ const opacity = Math.random() * 0.5 + 0.3;
619
+
620
+ ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
621
+ ctx.beginPath();
622
+ ctx.arc(x, y, size, 0, Math.PI * 2);
623
+ ctx.fill();
624
+ }
625
+ }
626
+
627
  // Game logic
628
  function update() {
629
  // Update direction from the next direction buffer
 
632
  // Move snake
633
  const head = {x: snake[0].x + direction.x, y: snake[0].y + direction.y};
634
 
635
+ // Check wall collision (unless invincible)
636
+ if (!invincible && (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount)) {
637
+ createParticles(
638
+ head.x * gridSize + gridSize / 2,
639
+ head.y * gridSize + gridSize / 2,
640
+ '#ef4444',
641
+ 30
642
+ );
643
  gameOver();
644
  return;
645
  }
646
 
647
+ // Wrap around if invincible
648
+ if (invincible) {
649
+ if (head.x < 0) head.x = tileCount - 1;
650
+ if (head.x >= tileCount) head.x = 0;
651
+ if (head.y < 0) head.y = tileCount - 1;
652
+ if (head.y >= tileCount) head.y = 0;
653
+ }
654
+
655
+ // Check self collision (unless invincible)
656
+ if (!invincible) {
657
+ for (let i = 1; i < snake.length; i++) {
658
+ if (head.x === snake[i].x && head.y === snake[i].y) {
659
+ createParticles(
660
+ head.x * gridSize + gridSize / 2,
661
+ head.y * gridSize + gridSize / 2,
662
+ '#ef4444',
663
+ 30
664
+ );
665
+ gameOver();
666
+ return;
667
+ }
668
  }
669
  }
670
 
 
673
 
674
  // Check food collision
675
  if (head.x === food.x && head.y === food.y) {
676
+ // Create food particles
677
+ createParticles(
678
+ food.x * gridSize + gridSize / 2,
679
+ food.y * gridSize + gridSize / 2,
680
+ null, // Random color
681
+ 20
682
+ );
683
+
684
  // Increase score
685
  score += 10;
686
  scoreElement.textContent = score;
687
 
688
+ // Generate new food (not on snake or power-ups)
689
  generateFood();
690
 
691
  // Increase speed slightly every 50 points
692
  if (score % 50 === 0 && speed < 15) {
693
  speed += 0.5;
694
  }
695
+
696
+ // Chance to spawn power-up
697
+ if (Math.random() < 0.3 && powerUps.length < 2) {
698
+ generatePowerUp();
699
+ }
700
  } else {
701
  // Remove tail if no food eaten
702
  snake.pop();
703
  }
704
+
705
+ // Check power-up collision
706
+ for (let i = powerUps.length - 1; i >= 0; i--) {
707
+ const pu = powerUps[i];
708
+ if (head.x === pu.x && head.y === pu.y) {
709
+ // Apply power-up effect
710
+ applyPowerUp(pu.type);
711
+
712
+ // Remove power-up
713
+ powerUps.splice(i, 1);
714
+
715
+ // Create particles
716
+ createParticles(
717
+ pu.x * gridSize + gridSize / 2,
718
+ pu.y * gridSize + gridSize / 2,
719
+ '#eab308',
720
+ 25
721
+ );
722
+ }
723
+ }
724
+
725
+ // Update power-up timer
726
+ if (powerUpTimer > 0) {
727
+ powerUpTimer--;
728
+ if (powerUpTimer === 0) {
729
+ invincible = false;
730
+ rainbowMode = false;
731
+ }
732
+ }
733
+
734
+ // Update rainbow timer
735
+ if (rainbowTimer > 0) {
736
+ rainbowTimer--;
737
+ if (rainbowTimer === 0) {
738
+ rainbowMode = false;
739
+ }
740
+ }
741
+ }
742
+
743
+ function applyPowerUp(type) {
744
+ switch(type) {
745
+ case 'invincible':
746
+ invincible = true;
747
+ powerUpTimer = 300; // 5 seconds at 60fps
748
+ break;
749
+ case 'rainbow':
750
+ rainbowMode = true;
751
+ rainbowTimer = 450; // 7.5 seconds at 60fps
752
+ break;
753
+ case 'speed':
754
+ speed = Math.max(5, speed - 2); // Slow down
755
+ powerUpTimer = 300;
756
+ break;
757
+ }
758
+
759
+ // Add score bonus
760
+ score += 50;
761
+ scoreElement.textContent = score;
762
  }
763
 
764
  function generateFood() {
 
770
  y: Math.floor(Math.random() * tileCount)
771
  };
772
 
773
+ // Check if food is on snake or power-ups
774
  validPosition = true;
775
+
776
  for (let segment of snake) {
777
  if (segment.x === food.x && segment.y === food.y) {
778
  validPosition = false;
779
  break;
780
  }
781
  }
782
+
783
+ if (validPosition) {
784
+ for (let pu of powerUps) {
785
+ if (pu.x === food.x && pu.y === food.y) {
786
+ validPosition = false;
787
+ break;
788
+ }
789
+ }
790
+ }
791
+ }
792
+ }
793
+
794
+ function generatePowerUp() {
795
+ let validPosition = false;
796
+ const types = ['invincible', 'rainbow', 'speed'];
797
+ const type = types[Math.floor(Math.random() * types.length)];
798
+
799
+ while (!validPosition) {
800
+ const pu = {
801
+ x: Math.floor(Math.random() * tileCount),
802
+ y: Math.floor(Math.random() * tileCount),
803
+ type: type
804
+ };
805
+
806
+ // Check if power-up is on snake or food
807
+ validPosition = true;
808
+
809
+ for (let segment of snake) {
810
+ if (segment.x === pu.x && segment.y === pu.y) {
811
+ validPosition = false;
812
+ break;
813
+ }
814
+ }
815
+
816
+ if (validPosition && (food.x === pu.x && food.y === pu.y)) {
817
+ validPosition = false;
818
+ }
819
+
820
+ if (validPosition) {
821
+ for (let existingPu of powerUps) {
822
+ if (existingPu.x === pu.x && existingPu.y === pu.y) {
823
+ validPosition = false;
824
+ break;
825
+ }
826
+ }
827
+ }
828
+
829
+ if (validPosition) {
830
+ powerUps.push(pu);
831
+ break;
832
+ }
833
  }
834
  }
835
 
836
  function gameOver() {
837
+ cancelAnimationFrame(gameLoop);
838
  gameRunning = false;
839
 
840
  // Update high score
 
845
  }
846
 
847
  finalScoreElement.textContent = score;
848
+ finalHighScoreElement.textContent = highScore;
849
  gameOverScreen.classList.remove('hidden');
850
  }
851
 
 
856
  score = 0;
857
  scoreElement.textContent = score;
858
  speed = 7;
859
+ powerUps = [];
860
+ particles = [];
861
+ trails = [];
862
+ invincible = false;
863
+ rainbowMode = false;
864
+ powerUpTimer = 0;
865
+ rainbowTimer = 0;
866
  generateFood();
867
  gameOverScreen.classList.add('hidden');
868
  }
869
 
870
+ function gameLoop(timestamp) {
871
+ if (!gameRunning) return;
872
+
873
+ const secondsSinceLastRender = (timestamp - lastRenderTime) / 1000;
874
+ if (secondsSinceLastRender < 1 / speed) {
875
+ requestAnimationFrame(gameLoop);
876
+ return;
877
+ }
878
+ lastRenderTime = timestamp;
879
+
880
+ drawBackground();
881
+ drawGrid();
882
+ updateParticles();
883
+ drawParticles();
884
+ drawPowerUps();
885
+ drawFood();
886
+ drawSnake();
887
+ update();
888
+
889
+ requestAnimationFrame(gameLoop);
890
+ }
891
+
892
  function startGame() {
893
  resetGame();
894
  startScreen.classList.add('hidden');
895
  gameRunning = true;
896
+ lastRenderTime = 0;
897
+ requestAnimationFrame(gameLoop);
 
 
 
 
 
 
898
  }
899
 
900
  // Event listeners
 
907
  // Prevent reverse direction
908
  switch (e.key) {
909
  case 'ArrowUp':
910
+ case 'w':
911
+ case 'W':
912
  if (direction.y === 0) nextDirection = {x: 0, y: -1};
913
  break;
914
  case 'ArrowDown':
915
+ case 's':
916
+ case 'S':
917
  if (direction.y === 0) nextDirection = {x: 0, y: 1};
918
  break;
919
  case 'ArrowLeft':
920
+ case 'a':
921
+ case 'A':
922
  if (direction.x === 0) nextDirection = {x: -1, y: 0};
923
  break;
924
  case 'ArrowRight':
925
+ case 'd':
926
+ case 'D':
927
  if (direction.x === 0) nextDirection = {x: 1, y: 0};
928
  break;
929
  }
930
  });
931
 
932
+ // Mobile control buttons
933
+ document.getElementById('upBtn').addEventListener('touchstart', (e) => {
934
+ e.preventDefault();
935
+ if (direction.y === 0) nextDirection = {x: 0, y: -1};
936
+ });
937
+
938
+ document.getElementById('downBtn').addEventListener('touchstart', (e) => {
939
+ e.preventDefault();
940
+ if (direction.y === 0) nextDirection = {x: 0, y: 1};
941
+ });
942
+
943
+ document.getElementById('leftBtn').addEventListener('touchstart', (e) => {
944
+ e.preventDefault();
945
+ if (direction.x === 0) nextDirection = {x: -1, y: 0};
946
+ });
947
+
948
+ document.getElementById('rightBtn').addEventListener('touchstart', (e) => {
949
+ e.preventDefault();
950
+ if (direction.x === 0) nextDirection = {x: 1, y: 0};
951
+ });
952
+
953
+ // Mouse controls for desktop (click to change direction)
954
+ canvas.addEventListener('click', (e) => {
955
+ if (!gameRunning) return;
956
+
957
+ const rect = canvas.getBoundingClientRect();
958
+ const clickX = e.clientX - rect.left;
959
+ const clickY = e.clientY - rect.top;
960
+ const headX = snake[0].x * gridSize + gridSize / 2;
961
+ const headY = snake[0].y * gridSize + gridSize / 2;
962
+
963
+ const diffX = clickX - headX;
964
+ const diffY = clickY - headY;
965
+
966
+ // Determine direction based on click position relative to head
967
+ if (Math.abs(diffX) > Math.abs(diffY)) {
968
+ // Horizontal movement
969
+ if (diffX > 0 && direction.x === 0) {
970
+ nextDirection = {x: 1, y: 0}; // Right
971
+ } else if (diffX < 0 && direction.x === 0) {
972
+ nextDirection = {x: -1, y: 0}; // Left
973
+ }
974
+ } else {
975
+ // Vertical movement
976
+ if (diffY > 0 && direction.y === 0) {
977
+ nextDirection = {x: 0, y: 1}; // Down
978
+ } else if (diffY < 0 && direction.y === 0) {
979
+ nextDirection = {x: 0, y: -1}; // Up
980
+ }
981
+ }
982
+ });
983
+
984
+ // Touch controls for mobile (swipe)
985
+ let touchStartX = 0;
986
+ let touchStartY = 0;
987
+
988
  canvas.addEventListener('touchstart', (e) => {
989
  touchStartX = e.touches[0].clientX;
990
  touchStartY = e.touches[0].clientY;
991
+ e.preventDefault();
992
+ }, { passive: false });
993
 
994
  canvas.addEventListener('touchmove', (e) => {
995
  if (!touchStartX || !touchStartY || !gameRunning) return;
 
1003
  // Determine swipe direction
1004
  if (Math.abs(diffX) > Math.abs(diffY)) {
1005
  // Horizontal swipe
1006
+ if (diffX > 30 && direction.x === 0) {
1007
  nextDirection = {x: -1, y: 0}; // Left swipe
1008
+ touchStartX = 0;
1009
+ touchStartY = 0;
1010
+ } else if (diffX < -30 && direction.x === 0) {
1011
  nextDirection = {x: 1, y: 0}; // Right swipe
1012
+ touchStartX = 0;
1013
+ touchStartY = 0;
1014
  }
1015
  } else {
1016
  // Vertical swipe
1017
+ if (diffY > 30 && direction.y === 0) {
1018
  nextDirection = {x: 0, y: -1}; // Up swipe
1019
+ touchStartX = 0;
1020
+ touchStartY = 0;
1021
+ } else if (diffY < -30 && direction.y === 0) {
1022
  nextDirection = {x: 0, y: 1}; // Down swipe
1023
+ touchStartX = 0;
1024
+ touchStartY = 0;
1025
  }
1026
  }
1027
 
 
 
 
1028
  e.preventDefault();
1029
+ }, { passive: false });
1030
 
1031
  // Button event listeners
1032
  startButton.addEventListener('click', startGame);
1033
  restartButton.addEventListener('click', startGame);
1034
 
1035
+ // Keyboard controls for buttons (accessibility)
1036
+ startButton.addEventListener('keydown', (e) => {
1037
+ if (e.key === 'Enter' || e.key === ' ') {
1038
+ startGame();
1039
+ }
1040
+ });
1041
+
1042
+ restartButton.addEventListener('keydown', (e) => {
1043
+ if (e.key === 'Enter' || e.key === ' ') {
1044
+ startGame();
1045
+ }
1046
+ });
1047
+
1048
  // Initial draw
1049
+ drawBackground();
1050
  drawGrid();
1051
  drawFood();
1052
  drawSnake();
1053
+
1054
+ // Show mobile controls only on touch devices
1055
+ if ('ontouchstart' in window || navigator.maxTouchPoints) {
1056
+ mobileControls.classList.remove('hidden');
1057
+ } else {
1058
+ mobileControls.classList.add('hidden');
1059
+ }
1060
  });
1061
  </script>
1062
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=pijou/snake" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>