Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Disco Snake</title>
- <style>
- :root {
- --board-size: min(78vmin, 620px);
- --tile-size: 44px;
- --bg1: #ff0080;
- --bg2: #00f0ff;
- --bg3: #ffe600;
- --bg4: #7cff00;
- --panel: rgba(8, 10, 20, 0.72);
- --line: rgba(255,255,255,0.14);
- --text: #f6f7fb;
- --shadow: 0 20px 60px rgba(0,0,0,0.35);
- }
- * { box-sizing: border-box; }
- html, body {
- margin: 0;
- min-height: 100%;
- font-family: Arial, Helvetica, sans-serif;
- color: var(--text);
- overflow: hidden;
- background: #090909;
- }
- body {
- display: grid;
- place-items: center;
- position: relative;
- isolation: isolate;
- }
- body::before,
- body::after {
- display: none;
- }
- body::before {
- background: #090909;
- filter: none;
- opacity: 1;
- animation: none;
- }
- body::after {
- background:
- linear-gradient(90deg, rgba(255,255,255,0.1) 2px, transparent 2px),
- linear-gradient(rgba(255,255,255,0.1) 2px, transparent 2px),
- linear-gradient(135deg, rgba(255,255,255,0.12), rgba(255,255,255,0));
- background-size: var(--tile-size) var(--tile-size), var(--tile-size) var(--tile-size), 100% 100%;
- animation: danceFloor 1.8s steps(1) infinite, floorMove 7s linear infinite;
- transform: perspective(1000px) rotateX(70deg) translateY(22vh) scale(1.7);
- opacity: 0.85;
- box-shadow: inset 0 0 120px rgba(255,255,255,0.08);
- }
- .wrap {
- width: min(92vw, 760px);
- display: grid;
- gap: 14px;
- justify-items: center;
- padding: 18px;
- }
- .hud {
- width: 100%;
- display: flex;
- justify-content: space-between;
- gap: 12px;
- padding: 14px 18px;
- border-radius: 20px;
- background: var(--panel);
- border: 1px solid rgba(255,255,255,0.1);
- box-shadow: var(--shadow);
- backdrop-filter: blur(10px);
- align-items: center;
- flex-wrap: wrap;
- }
- .title {
- font-size: clamp(1.2rem, 2vw, 1.7rem);
- font-weight: 700;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- text-shadow: 0 0 14px rgba(255, 0, 200, 0.45);
- }
- .stats {
- display: flex;
- gap: 16px;
- flex-wrap: wrap;
- font-size: 0.98rem;
- }
- .pill {
- padding: 8px 12px;
- border-radius: 999px;
- background: rgba(255,255,255,0.08);
- border: 1px solid rgba(255,255,255,0.08);
- box-shadow: inset 0 0 12px rgba(255,255,255,0.04);
- }
- .board-shell {
- width: var(--board-size);
- height: var(--board-size);
- position: relative;
- border-radius: 28px;
- overflow: hidden;
- box-shadow: var(--shadow);
- border: 1px solid rgba(255,255,255,0.12);
- background: rgba(8, 8, 14, 0.72);
- backdrop-filter: blur(10px);
- }
- canvas {
- width: 100%;
- height: 100%;
- display: block;
- }
- .overlay {
- position: absolute;
- inset: 0;
- display: grid;
- place-items: center;
- background: linear-gradient(rgba(3,4,10,0.16), rgba(3,4,10,0.72));
- text-align: center;
- padding: 28px;
- transition: opacity 0.25s ease;
- }
- .overlay.hidden {
- opacity: 0;
- pointer-events: none;
- }
- .card {
- max-width: 420px;
- background: rgba(12, 14, 24, 0.78);
- border: 1px solid rgba(255,255,255,0.12);
- border-radius: 24px;
- padding: 24px;
- box-shadow: var(--shadow);
- backdrop-filter: blur(14px);
- }
- .card h1 {
- margin: 0 0 10px;
- font-size: clamp(1.8rem, 4vw, 2.6rem);
- line-height: 1;
- text-shadow: 0 0 20px rgba(255, 0, 220, 0.45), 0 0 28px rgba(0, 220, 255, 0.35);
- }
- .card p {
- margin: 10px 0;
- line-height: 1.45;
- color: rgba(255,255,255,0.88);
- }
- .keys {
- display: inline-flex;
- gap: 8px;
- flex-wrap: wrap;
- justify-content: center;
- margin-top: 10px;
- }
- .key {
- min-width: 40px;
- padding: 8px 10px;
- border-radius: 12px;
- background: rgba(255,255,255,0.08);
- border: 1px solid rgba(255,255,255,0.14);
- font-weight: 700;
- box-shadow: 0 0 10px rgba(255,255,255,0.06);
- }
- .button {
- display: inline-block;
- margin-top: 14px;
- padding: 12px 18px;
- border: none;
- border-radius: 999px;
- background: linear-gradient(135deg, #ff00cc, #00ddff);
- color: white;
- font-weight: 700;
- letter-spacing: 0.04em;
- cursor: pointer;
- box-shadow: 0 10px 30px rgba(0,0,0,0.28), 0 0 18px rgba(255,0,200,0.25);
- }
- .footer {
- font-size: 0.92rem;
- color: rgba(255,255,255,0.82);
- text-align: center;
- text-shadow: 0 2px 12px rgba(0,0,0,0.4);
- }
- @keyframes hueShift {
- 0% { opacity: 1; }
- 100% { opacity: 1; }
- }
- 50% { background-position: 100% 50%; filter: hue-rotate(180deg) saturate(1.5) blur(14px); }
- 100% { background-position: 0% 50%; filter: hue-rotate(360deg) saturate(1.3) blur(10px); }
- }
- @keyframes danceFloor {
- 0% { opacity: 1; }
- 100% { opacity: 1; }
- }
- 20% { filter: hue-rotate(45deg) brightness(1.25); }
- 40% { filter: hue-rotate(110deg) brightness(0.95); }
- 60% { filter: hue-rotate(180deg) brightness(1.3); }
- 80% { filter: hue-rotate(260deg) brightness(1.05); }
- 100% { filter: hue-rotate(360deg) brightness(1.2); }
- }
- @keyframes floorMove {
- 0% { opacity: 1; }
- 100% { opacity: 1; }
- }
- to { background-position: 44px 0, 0 44px, 0 0; }
- }
- @media (max-width: 640px) {
- :root { --tile-size: 34px; }
- .hud { justify-content: center; }
- }
- </style>
- </head>
- <body>
- <div class="wrap">
- <div class="hud">
- <div class="title">Disco Snake</div>
- <div class="stats">
- <div class="pill">Score: <strong id="score">0</strong></div>
- <div class="pill">Best: <strong id="best">0</strong></div>
- <div class="pill">Speed: <strong id="speed">7</strong></div>
- </div>
- </div>
- <div class="board-shell">
- <canvas id="game" width="600" height="600"></canvas>
- <div class="overlay" id="overlay">
- <div class="card">
- <h1>Disco Snake</h1>
- <p>Eat the glowing fruit, grow longer, and survive on the flashing dance floor.</p>
- <p>Use arrow keys or WASD. Press space to pause or resume.</p>
- <div class="keys">
- <span class="key">↑</span>
- <span class="key">↓</span>
- <span class="key">←</span>
- <span class="key">→</span>
- <span class="key">WASD</span>
- </div>
- <br />
- <button class="button" id="startBtn">Start Game</button>
- </div>
- </div>
- </div>
- <div class="footer">Single-file HTML game with a color-animated disco floor background.</div>
- </div>
- <script>
- const canvas = document.getElementById('game');
- const ctx = canvas.getContext('2d');
- const scoreEl = document.getElementById('score');
- const bestEl = document.getElementById('best');
- const speedEl = document.getElementById('speed');
- const overlay = document.getElementById('overlay');
- const startBtn = document.getElementById('startBtn');
- const gridCount = 20;
- const cell = canvas.width / gridCount;
- const initialSpeed = 7;
- const maxSpeed = 16;
- let best = Number(localStorage.getItem('discoSnakeBest') || 0);
- bestEl.textContent = best;
- let snake, dir, nextDir, food, score, speed, running, paused, loopHandle;
- function randomCell(exclude = []) {
- let p;
- do {
- p = {
- x: Math.floor(Math.random() * gridCount),
- y: Math.floor(Math.random() * gridCount)
- };
- } while (exclude.some(seg => seg.x === p.x && seg.y === p.y));
- return p;
- }
- function resetGame() {
- snake = [
- { x: 10, y: 10 },
- { x: 9, y: 10 },
- { x: 8, y: 10 }
- ];
- dir = { x: 1, y: 0 };
- nextDir = { x: 1, y: 0 };
- food = randomCell(snake);
- score = 0;
- speed = initialSpeed;
- running = true;
- paused = false;
- updateHud();
- overlay.classList.add('hidden');
- cancelAnimationFrame(loopHandle);
- lastStep = 0;
- accumulator = 0;
- loopHandle = requestAnimationFrame(gameLoop);
- }
- function updateHud() {
- scoreEl.textContent = score;
- bestEl.textContent = best;
- speedEl.textContent = speed;
- }
- function setDirection(x, y) {
- if (!running || paused) return;
- if (x === -dir.x && y === -dir.y) return;
- nextDir = { x, y };
- }
- function step() {
- dir = nextDir;
- const head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
- if (head.x < 0) head.x = gridCount - 1;
- else if (head.x >= gridCount) head.x = 0;
- if (head.y < 0) head.y = gridCount - 1;
- else if (head.y >= gridCount) head.y = 0;
- if (snake.some(seg => seg.x === head.x && seg.y === head.y)) {
- endGame();
- return;
- }
- snake.unshift(head);
- if (head.x === food.x && head.y === food.y) {
- score += 10;
- if (score > best) {
- best = score;
- localStorage.setItem('discoSnakeBest', String(best));
- }
- speed = Math.min(maxSpeed, initialSpeed + Math.floor(score / 40));
- food = randomCell(snake);
- pulseBackground();
- } else {
- snake.pop();
- }
- updateHud();
- }
- function endGame() {
- running = false;
- paused = false;
- overlay.classList.remove('hidden');
- overlay.querySelector('h1').textContent = 'Game Over';
- overlay.querySelector('p').textContent = `Your score: ${score}. Ready for another round on the dance floor?`;
- startBtn.textContent = 'Restart';
- }
- function pulseBackground() {
- document.body.animate(
- [
- { filter: 'brightness(1)' },
- { filter: 'brightness(1.24)' },
- { filter: 'brightness(1)' }
- ],
- { duration: 260, easing: 'ease-out' }
- );
- canvas.animate(
- [
- { transform: 'scale(1)' },
- { transform: 'scale(1.012)' },
- { transform: 'scale(1)' }
- ],
- { duration: 180, easing: 'ease-out' }
- );
- }
- function drawBoard() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- const t = performance.now() * 0.0015;
- const globalWave = Math.sin(t * 1.7) * 18 + Math.cos(t * 1.1) * 10;
- for (let y = 0; y < gridCount; y++) {
- for (let x = 0; x < gridCount; x++) {
- const dx = x - gridCount / 2;
- const dy = y - gridCount / 2;
- const dist = Math.sqrt(dx * dx + dy * dy);
- const v =
- Math.sin(x * 0.38 + t * 2.1) +
- Math.sin(y * 0.42 + t * 1.8) +
- Math.sin((x + y) * 0.26 + t * 1.9) +
- Math.sin(dist * 0.42 - t * 2.2);
- const normalized = (v + 4) / 8;
- const hue = (210 + globalWave + normalized * 70) % 360;
- const light = 30 + normalized * 18;
- const sat = 78 + normalized * 8;
- ctx.fillStyle = `hsl(${hue}, ${sat}%, ${light}%)`;
- ctx.fillRect(x * cell, y * cell, cell, cell);
- const shine = ctx.createLinearGradient(x * cell, y * cell, x * cell + cell, y * cell + cell);
- shine.addColorStop(0, 'rgba(255,255,255,0.16)');
- shine.addColorStop(0.22, 'rgba(255,255,255,0.06)');
- shine.addColorStop(0.55, 'rgba(255,255,255,0.015)');
- shine.addColorStop(1, 'rgba(0,0,0,0.16)');
- ctx.fillStyle = shine;
- ctx.fillRect(x * cell, y * cell, cell, cell);
- }
- }
- ctx.strokeStyle = 'rgba(255,255,255,0.12)';
- ctx.lineWidth = 1;
- for (let i = 0; i <= gridCount; i++) {
- const p = i * cell;
- ctx.beginPath();
- ctx.moveTo(p, 0);
- ctx.lineTo(p, canvas.height);
- ctx.stroke();
- ctx.beginPath();
- ctx.moveTo(0, p);
- ctx.lineTo(canvas.width, p);
- ctx.stroke();
- }
- const vignette = ctx.createRadialGradient(
- canvas.width / 2,
- canvas.height / 2,
- canvas.width * 0.12,
- canvas.width / 2,
- canvas.height / 2,
- canvas.width * 0.78
- );
- vignette.addColorStop(0, 'rgba(255,255,255,0)');
- vignette.addColorStop(1, 'rgba(0,0,0,0.28)');
- ctx.fillStyle = vignette;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- }
- function drawFood() {
- const px = food.x * cell + cell / 2;
- const py = food.y * cell + cell / 2;
- const pulse = 1 + Math.sin(performance.now() * 0.01) * 0.08;
- const radius = cell * 0.28 * pulse;
- const glow = ctx.createRadialGradient(px, py, 3, px, py, radius * 2.4);
- glow.addColorStop(0, 'rgba(255,255,255,1)');
- glow.addColorStop(0.18, 'rgba(255,240,120,0.98)');
- glow.addColorStop(0.5, 'rgba(255,100,180,0.82)');
- glow.addColorStop(1, 'rgba(255,40,120,0)');
- ctx.fillStyle = glow;
- ctx.beginPath();
- ctx.arc(px, py, radius * 2.4, 0, Math.PI * 2);
- ctx.fill();
- ctx.fillStyle = '#fff07a';
- ctx.beginPath();
- ctx.arc(px, py, radius, 0, Math.PI * 2);
- ctx.fill();
- }
- function drawSnake() {
- const now = performance.now() * 0.12;
- snake.forEach((seg, index) => {
- const x = seg.x * cell;
- const y = seg.y * cell;
- const inset = 3;
- const w = cell - inset * 2;
- const h = cell - inset * 2;
- const radius = 10;
- const tailRatio = snake.length > 1 ? index / (snake.length - 1) : 0;
- const g = ctx.createLinearGradient(x, y, x + cell, y + cell);
- if (index === 0) {
- g.addColorStop(0, '#9aff6a');
- g.addColorStop(1, '#00f7c2');
- } else {
- const hueA = (300 + now + tailRatio * 140) % 360;
- const hueB = (hueA + 42) % 360;
- const lightA = 62 - tailRatio * 10;
- const lightB = 54 - tailRatio * 8;
- g.addColorStop(0, `hsl(${hueA}, 95%, ${lightA}%)`);
- g.addColorStop(1, `hsl(${hueB}, 92%, ${lightB}%)`);
- }
- ctx.save();
- ctx.shadowBlur = index === 0 ? 24 : 18;
- ctx.shadowColor = index === 0
- ? 'rgba(0,255,210,0.75)'
- : `hsla(${(320 + now + tailRatio * 120) % 360}, 100%, 65%, 0.55)`;
- ctx.fillStyle = g;
- roundRect(ctx, x + inset, y + inset, w, h, radius);
- ctx.fill();
- ctx.restore();
- ctx.strokeStyle = index === 0 ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.14)';
- ctx.lineWidth = 1;
- roundRect(ctx, x + inset, y + inset, w, h, radius);
- ctx.stroke();
- if (index > 0) {
- ctx.fillStyle = `rgba(255,255,255,${0.05 - tailRatio * 0.02})`;
- roundRect(ctx, x + inset + 2, y + inset + 2, w - 10, h * 0.32, 6);
- ctx.fill();
- }
- if (index === 0) {
- const eyeOffsetX = dir.x !== 0 ? (dir.x > 0 ? 21 : 11) : 10;
- const eyeOffsetY1 = dir.y !== 0 ? (dir.y > 0 ? 21 : 11) : 11;
- const eyeOffsetY2 = dir.y !== 0 ? (dir.y > 0 ? 21 : 11) : 21;
- const eyeX1 = x + eyeOffsetX;
- const eyeX2 = dir.y !== 0 ? x + 21 : x + eyeOffsetX;
- ctx.fillStyle = '#121212';
- ctx.beginPath();
- ctx.arc(eyeX1, y + eyeOffsetY1, 2.8, 0, Math.PI * 2);
- ctx.arc(eyeX2, y + eyeOffsetY2, 2.8, 0, Math.PI * 2);
- ctx.fill();
- }
- });
- }
- function roundRect(ctx, x, y, width, height, radius) {
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
- }
- let lastStep = 0;
- let accumulator = 0;
- function gameLoop(timestamp) {
- if (!running) {
- function sparkles() {
- const now = performance.now() * 0.004;
- for (let i = 0; i < 10; i++) {
- const x = (Math.sin(now + i * 1.7) * 0.5 + 0.5) * canvas.width;
- const y = (Math.cos(now * 1.2 + i * 2.3) * 0.5 + 0.5) * canvas.height;
- const r = 1.5 + ((i % 3) * 0.8);
- ctx.fillStyle = `rgba(255,255,255,${0.08 + (i % 4) * 0.03})`;
- ctx.beginPath();
- ctx.arc(x, y, r, 0, Math.PI * 2);
- ctx.fill();
- }
- }
- const originalDraw = draw;
- draw = function() {
- drawBoard();
- sparkles();
- drawFood();
- drawSnake();
- if (paused && running) {
- ctx.fillStyle = 'rgba(8, 8, 12, 0.5)';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = '#ffffff';
- ctx.font = 'bold 48px Arial';
- ctx.textAlign = 'center';
- ctx.fillText('PAUSED', canvas.width / 2, canvas.height / 2);
- }
- }
- draw();
- return;
- }
- if (!lastStep) lastStep = timestamp;
- const delta = timestamp - lastStep;
- lastStep = timestamp;
- accumulator += delta;
- const interval = 1000 / speed;
- while (accumulator >= interval) {
- if (!paused) step();
- accumulator -= interval;
- }
- draw();
- loopHandle = requestAnimationFrame(gameLoop);
- }
- function draw() {
- drawBoard();
- drawFood();
- drawSnake();
- if (paused && running) {
- ctx.fillStyle = 'rgba(8, 8, 12, 0.5)';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = '#ffffff';
- ctx.font = 'bold 48px Arial';
- ctx.textAlign = 'center';
- ctx.fillText('PAUSED', canvas.width / 2, canvas.height / 2);
- }
- }
- document.addEventListener('keydown', (e) => {
- const key = e.key.toLowerCase();
- if (["arrowup", "arrowdown", "arrowleft", "arrowright", " ", "w", "a", "s", "d"].includes(key)) {
- e.preventDefault();
- }
- if (key === 'arrowup' || key === 'w') setDirection(0, -1);
- else if (key === 'arrowdown' || key === 's') setDirection(0, 1);
- else if (key === 'arrowleft' || key === 'a') setDirection(-1, 0);
- else if (key === 'arrowright' || key === 'd') setDirection(1, 0);
- else if (key === ' ') {
- if (running) paused = !paused;
- }
- });
- startBtn.addEventListener('click', () => {
- overlay.querySelector('h1').textContent = 'Disco Snake';
- overlay.querySelector('p').textContent = 'Eat the glowing fruit, grow longer, and survive on the flashing dance floor.';
- startBtn.textContent = 'Start Game';
- resetGame();
- });
- draw();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment