neonsanke / index.html
engerl's picture
Add 2 files
feb3e3e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Snake - Ultimate Snake Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Orbitron:wght@400;700&display=swap');
:root {
--neon-green: #39ff14;
--neon-pink: #ff2ced;
--neon-blue: #00e5ff;
--neon-purple: #9d00ff;
--dark-bg: #0f0f1a;
}
body {
font-family: 'Orbitron', sans-serif;
background-color: var(--dark-bg);
color: white;
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.game-container {
position: relative;
width: 90vmin;
height: 90vmin;
max-width: 600px;
max-height: 600px;
border: 4px solid var(--neon-purple);
border-radius: 8px;
box-shadow: 0 0 20px var(--neon-purple),
inset 0 0 20px var(--neon-purple);
overflow: hidden;
}
#game-canvas {
width: 100%;
height: 100%;
background-color: rgba(15, 15, 26, 0.8);
}
.snake-segment {
position: absolute;
width: 20px;
height: 20px;
background-color: var(--neon-green);
border-radius: 4px;
box-shadow: 0 0 10px var(--neon-green);
transition: transform 0.1s ease-out;
}
.snake-head {
background-color: var(--neon-blue);
box-shadow: 0 0 15px var(--neon-blue);
z-index: 10;
}
.food {
position: absolute;
width: 20px;
height: 20px;
background-color: var(--neon-pink);
border-radius: 50%;
box-shadow: 0 0 15px var(--neon-pink);
animation: pulse 1.5s infinite alternate;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(1.2); opacity: 0.8; }
}
.score-display {
font-family: 'Press Start 2P', cursive;
font-size: 1.5rem;
color: var(--neon-green);
text-shadow: 0 0 10px var(--neon-green);
margin-bottom: 1rem;
}
.controls {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.btn {
background: transparent;
color: white;
border: 2px solid var(--neon-blue);
padding: 0.5rem 1rem;
border-radius: 4px;
font-family: 'Orbitron', sans-serif;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 0 10px var(--neon-blue);
}
.btn:hover {
background: var(--neon-blue);
color: black;
transform: translateY(-2px);
box-shadow: 0 0 20px var(--neon-blue);
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 100;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
}
.modal.active {
opacity: 1;
pointer-events: all;
}
.modal-content {
background-color: var(--dark-bg);
border: 3px solid var(--neon-purple);
border-radius: 8px;
padding: 2rem;
max-width: 500px;
width: 90%;
text-align: center;
box-shadow: 0 0 30px var(--neon-purple);
transform: scale(0.9);
transition: transform 0.3s;
}
.modal.active .modal-content {
transform: scale(1);
}
.modal-title {
color: var(--neon-green);
font-size: 2rem;
margin-bottom: 1rem;
text-shadow: 0 0 10px var(--neon-green);
}
.game-over {
color: var(--neon-pink);
font-size: 3rem;
margin-bottom: 1rem;
text-shadow: 0 0 15px var(--neon-pink);
animation: blink 0.5s infinite alternate;
}
@keyframes blink {
0% { opacity: 1; }
100% { opacity: 0.5; }
}
.grid-lines {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(57, 255, 20, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(57, 255, 20, 0.05) 1px, transparent 1px);
background-size: 20px 20px;
pointer-events: none;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background-color: var(--neon-blue);
border-radius: 50%;
pointer-events: none;
opacity: 0;
}
@media (max-width: 768px) {
.controls {
flex-direction: column;
gap: 0.5rem;
}
.score-display {
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="grid-lines"></div>
<h1 class="text-4xl mb-4 font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-blue-500">
NEON SNAKE
</h1>
<div class="score-display">
SCORE: <span id="score">0</span> | HIGH: <span id="high-score">0</span>
</div>
<div class="game-container">
<canvas id="game-canvas"></canvas>
<div id="game-elements"></div>
</div>
<div class="controls mt-6">
<button id="start-btn" class="btn">
<i class="fas fa-play mr-2"></i> START
</button>
<button id="pause-btn" class="btn" disabled>
<i class="fas fa-pause mr-2"></i> PAUSE
</button>
<button id="restart-btn" class="btn">
<i class="fas fa-redo mr-2"></i> RESTART
</button>
</div>
<div class="modal" id="game-over-modal">
<div class="modal-content">
<div class="game-over">GAME OVER!</div>
<h2 class="modal-title">Your Score: <span id="final-score">0</span></h2>
<p class="text-white mb-4">Try again to beat your high score!</p>
<button id="play-again-btn" class="btn">
<i class="fas fa-gamepad mr-2"></i> PLAY AGAIN
</button>
</div>
</div>
<div class="absolute bottom-4 right-4 text-xs text-gray-500">
Use arrow keys or swipe to control
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Game elements
const canvas = document.getElementById('game-canvas');
const ctx = canvas.getContext('2d');
const gameElements = document.getElementById('game-elements');
const scoreDisplay = document.getElementById('score');
const highScoreDisplay = document.getElementById('high-score');
const finalScoreDisplay = document.getElementById('final-score');
// Buttons
const startBtn = document.getElementById('start-btn');
const pauseBtn = document.getElementById('pause-btn');
const restartBtn = document.getElementById('restart-btn');
const playAgainBtn = document.getElementById('play-again-btn');
// Modal
const gameOverModal = document.getElementById('game-over-modal');
// Game settings
const gridSize = 20;
let canvasSize = Math.min(600, window.innerWidth - 40, window.innerHeight - 200);
canvas.width = canvasSize;
canvas.height = canvasSize;
// Game state
let snake = [];
let food = {};
let direction = 'right';
let nextDirection = 'right';
let gameSpeed = 150;
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameInterval;
let isPaused = false;
let isGameOver = false;
let isGameRunning = false;
// Initialize game
function initGame() {
// Clear any existing game elements
gameElements.innerHTML = '';
// Reset snake
snake = [
{x: 5, y: 10},
{x: 4, y: 10},
{x: 3, y: 10}
];
// Reset direction
direction = 'right';
nextDirection = 'right';
// Reset score
score = 0;
scoreDisplay.textContent = score;
highScoreDisplay.textContent = highScore;
// Generate food
generateFood();
// Reset game state
isGameOver = false;
isPaused = false;
// Hide modal if visible
gameOverModal.classList.remove('active');
// Render initial state
render();
}
// Start game
function startGame() {
if (!isGameRunning) {
isGameRunning = true;
startBtn.disabled = true;
pauseBtn.disabled = false;
gameInterval = setInterval(gameLoop, gameSpeed);
}
}
// Pause game
function pauseGame() {
if (isGameRunning && !isPaused) {
isPaused = true;
clearInterval(gameInterval);
pauseBtn.innerHTML = '<i class="fas fa-play mr-2"></i> RESUME';
} else if (isGameRunning && isPaused) {
isPaused = false;
gameInterval = setInterval(gameLoop, gameSpeed);
pauseBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> PAUSE';
}
}
// Game over
function gameOver() {
isGameOver = true;
isGameRunning = false;
clearInterval(gameInterval);
startBtn.disabled = false;
pauseBtn.disabled = true;
// Update high score
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
highScoreDisplay.textContent = highScore;
}
// Show game over modal
finalScoreDisplay.textContent = score;
gameOverModal.classList.add('active');
// Create explosion effect
createExplosion(snake[0].x * gridSize, snake[0].y * gridSize);
}
// Game loop
function gameLoop() {
if (isPaused || isGameOver) return;
// Update direction
direction = nextDirection;
// Move snake
const head = {...snake[0]};
switch (direction) {
case 'up':
head.y -= 1;
break;
case 'down':
head.y += 1;
break;
case 'left':
head.x -= 1;
break;
case 'right':
head.x += 1;
break;
}
// Check collisions
if (
head.x < 0 ||
head.x >= canvas.width / gridSize ||
head.y < 0 ||
head.y >= canvas.height / gridSize ||
snake.some(segment => segment.x === head.x && segment.y === head.y)
) {
gameOver();
return;
}
// Add new head
snake.unshift(head);
// Check if snake ate food
if (head.x === food.x && head.y === food.y) {
// Increase score
score += 10;
scoreDisplay.textContent = score;
// Increase speed slightly
if (gameSpeed > 50 && score % 50 === 0) {
gameSpeed -= 5;
clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, gameSpeed);
}
// Generate new food
generateFood();
// Create particle effect
createParticles(food.x * gridSize + gridSize/2, food.y * gridSize + gridSize/2);
} else {
// Remove tail if no food was eaten
snake.pop();
}
// Render game
render();
}
// Generate food at random position
function generateFood() {
let newFood;
do {
newFood = {
x: Math.floor(Math.random() * (canvas.width / gridSize)),
y: Math.floor(Math.random() * (canvas.height / gridSize))
};
} while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
food = newFood;
// Create food element
const foodElement = document.createElement('div');
foodElement.className = 'food';
foodElement.style.left = `${food.x * gridSize}px`;
foodElement.style.top = `${food.y * gridSize}px`;
foodElement.style.width = `${gridSize}px`;
foodElement.style.height = `${gridSize}px`;
foodElement.id = 'food';
gameElements.appendChild(foodElement);
}
// Render game
function render() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw snake
snake.forEach((segment, index) => {
const segmentElement = document.createElement('div');
segmentElement.className = index === 0 ? 'snake-segment snake-head' : 'snake-segment';
segmentElement.style.left = `${segment.x * gridSize}px`;
segmentElement.style.top = `${segment.y * gridSize}px`;
segmentElement.style.width = `${gridSize}px`;
segmentElement.style.height = `${gridSize}px`;
segmentElement.id = `segment-${index}`;
// Update or create element
const existingElement = document.getElementById(`segment-${index}`);
if (existingElement) {
existingElement.style.left = `${segment.x * gridSize}px`;
existingElement.style.top = `${segment.y * gridSize}px`;
} else {
gameElements.appendChild(segmentElement);
}
});
// Remove extra segments if snake got shorter
const segmentElements = document.querySelectorAll('.snake-segment');
if (segmentElements.length > snake.length) {
for (let i = snake.length; i < segmentElements.length; i++) {
segmentElements[i].remove();
}
}
// Update food position
const foodElement = document.getElementById('food');
if (foodElement) {
foodElement.style.left = `${food.x * gridSize}px`;
foodElement.style.top = `${food.y * gridSize}px`;
}
}
// Create particles
function createParticles(x, y) {
for (let i = 0; i < 15; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
// Random direction and speed
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 3 + 1;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
document.body.appendChild(particle);
// Animate particle
let opacity = 1;
let size = 4;
let posX = x;
let posY = y;
const animate = () => {
opacity -= 0.02;
size -= 0.1;
posX += vx;
posY += vy;
if (opacity <= 0 || size <= 0) {
particle.remove();
return;
}
particle.style.opacity = opacity;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${posX}px`;
particle.style.top = `${posY}px`;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
}
// Create explosion effect
function createExplosion(x, y) {
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.backgroundColor = i % 2 === 0 ? 'var(--neon-pink)' : 'var(--neon-blue)';
particle.style.left = `${x + gridSize/2}px`;
particle.style.top = `${y + gridSize/2}px`;
// Random direction and speed
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 5 + 2;
const vx = Math.cos(angle) * speed;
const vy = Math.sin(angle) * speed;
document.body.appendChild(particle);
// Animate particle
let opacity = 1;
let size = Math.random() * 6 + 2;
let posX = x + gridSize/2;
let posY = y + gridSize/2;
const animate = () => {
opacity -= 0.02;
size -= 0.1;
posX += vx;
posY += vy;
if (opacity <= 0 || size <= 0) {
particle.remove();
return;
}
particle.style.opacity = opacity;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${posX}px`;
particle.style.top = `${posY}px`;
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
}
// Handle keyboard input
function handleKeyDown(e) {
if (!isGameRunning || isPaused) return;
switch (e.key) {
case 'ArrowUp':
if (direction !== 'down') nextDirection = 'up';
break;
case 'ArrowDown':
if (direction !== 'up') nextDirection = 'down';
break;
case 'ArrowLeft':
if (direction !== 'right') nextDirection = 'left';
break;
case 'ArrowRight':
if (direction !== 'left') nextDirection = 'right';
break;
}
}
// Handle touch events for mobile
let touchStartX = 0;
let touchStartY = 0;
function handleTouchStart(e) {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}
function handleTouchEnd(e) {
if (!isGameRunning || isPaused) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const dx = touchEndX - touchStartX;
const dy = touchEndY - touchStartY;
// Determine swipe direction
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
if (dx > 0 && direction !== 'left') {
nextDirection = 'right';
} else if (dx < 0 && direction !== 'right') {
nextDirection = 'left';
}
} else {
// Vertical swipe
if (dy > 0 && direction !== 'up') {
nextDirection = 'down';
} else if (dy < 0 && direction !== 'down') {
nextDirection = 'up';
}
}
}
// Event listeners
startBtn.addEventListener('click', startGame);
pauseBtn.addEventListener('click', pauseGame);
restartBtn.addEventListener('click', initGame);
playAgainBtn.addEventListener('click', () => {
gameOverModal.classList.remove('active');
initGame();
startGame();
});
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('touchstart', handleTouchStart);
document.addEventListener('touchend', handleTouchEnd);
// Handle window resize
window.addEventListener('resize', () => {
canvasSize = Math.min(600, window.innerWidth - 40, window.innerHeight - 200);
canvas.width = canvasSize;
canvas.height = canvasSize;
render();
});
// Initialize game
initGame();
});
</script>
<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=engerl/neonsanke" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>