Make touchscreen better and screen larger: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover" />
<title>Touchscreen Snake Game with 3 Apples</title>
<style>
  /* Reset */
  * {
    box-sizing: border-box;
  }
  html, body {
    margin: 0; padding: 0;
    height: 100%;
    background: #111;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    overscroll-behavior: none;
    user-select: none;
  }
  body {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -webkit-tap-highlight-color: transparent;
    height: 100vh;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    background: linear-gradient(45deg, #0f2027, #203a43, #2c5364);
  }
  #game {
    position: relative;
    width: 100vmin;
    height: 100vmin;
    max-width: 480px;
    max-height: 480px;
    border: 4px solid #00ff99;
    border-radius: 20px;
    background: #111;
    box-shadow: 0 0 20px #00ff99aa inset;
  }
  canvas {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 20px;
    background: #111;
  }
  #scoreboard {
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    color: #00ff99;
    font-weight: 700;
    font-size: 1.5rem;
    text-shadow: 0 0 15px #00ff99;
    user-select: none;
    pointer-events: none;
    z-index: 10;
  }

  /* Controls container with 4 buttons spaced as requested */
  #controls {
    width: 100%;
    max-width: 480px;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    padding: 12px 10px;
    background: #0c1a20;
    border-top: 2px solid #00ff99;
    box-shadow: 0 -5px 15px #00ff9977;
    border-radius: 0 0 20px 20px;
    user-select: none;
    gap: 12px;
  }
  .btn {
    background: #00ff99;
    border: none;
    border-radius: 12px;
    padding: 18px 0;
    color: #111;
    font-weight: 700;
    font-size: 1.6rem;
    box-shadow: 0 0 20px #00ff99;
    cursor: pointer;
    user-select: none;
    transition: background 0.2s ease, box-shadow 0.3s ease;
    width: 100%;
    max-width: none;
    text-align: center;
  }
  .btn:active {
    background: #00cc77;
    box-shadow: 0 0 30px #00cc77 inset;
  }
  /* Position buttons as per requirement:
     Left button on far left (grid column 1)
     Up button second from left (grid column 2)
     Down button second from right (grid column 3)
     Right button far right (grid column 4)
  */
  button[data-dir="left"] {
    grid-column: 1 / 2;
  }
  button[data-dir="up"] {
    grid-column: 2 / 3;
  }
  button[data-dir="down"] {
    grid-column: 3 / 4;
  }
  button[data-dir="right"] {
    grid-column: 4 / 5;
  }

  @media (max-width: 400px) {
    .btn {
      font-size: 1.3rem;
      padding: 14px 0;
    }
  }
</style>
</head>
<body>

<div id="game" style="touch-action:none;">
  <canvas id="canvas" width="480" height="480"></canvas>
  <div id="scoreboard">Score: 0</div>
</div>

<div id="controls" aria-label="Directional controls">
  <button class="btn" data-dir="left" aria-label="Move left">←</button>
  <button class="btn" data-dir="up" aria-label="Move up">↑</button>
  <button class="btn" data-dir="down" aria-label="Move down">↓</button>
  <button class="btn" data-dir="right" aria-label="Move right">→</button>
</div>

<script>
(() => {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  const scoreboard = document.getElementById('scoreboard');
  const controls = document.querySelectorAll('.btn');

  const gridSize = 20; // size of one square
  const tileCount = canvas.width / gridSize; // 24x24 grid

  // Initial snake state
  let snake = [
    { x: 12, y: 12 },
    { x: 11, y: 12 },
    { x: 10, y: 12 }
  ];
  let velocity = { x: 1, y: 0 };
  let nextVelocity = { x: 1, y: 0 };
  let apples = [];
  const maxApples = 3;
  let score = 0;
  let gameOver = false;

  // Create gradients every frame for dynamic glow effect
  function createSnakeGradient() {
    const grad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
    grad.addColorStop(0, "#00ff99");
    grad.addColorStop(1, "#00ffaa");
    return grad;
  }
  function createAppleGradient() {
    const grad = ctx.createRadialGradient(0, 0, 5, 0, 0, 12);
    grad.addColorStop(0, "#ff4444");
    grad.addColorStop(1, "#990000");
    return grad;
  }

  // Draw rounded rect helper
  function roundedRect(x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h - r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
    ctx.fill();
  }

  // Spawn apples on empty tiles
  function spawnApples() {
    while (apples.length < maxApples) {
      let pos = {
        x: Math.floor(Math.random() * tileCount),
        y: Math.floor(Math.random() * tileCount)
      };
      if (isCellEmpty(pos.x, pos.y)) {
        apples.push(pos);
      }
    }
  }

  // Check if a tile is free (no snake, no apple)
  function isCellEmpty(x, y) {
    for (let s of snake) {
      if (s.x === x && s.y === y) return false;
    }
    for (let a of apples) {
      if (a.x === x && a.y === y) return false;
    }
    return true;
  }

  // Draw everything
  function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Background subtle grid lines
    ctx.strokeStyle = "#222";
    ctx.lineWidth = 1;
    for (let i = 0; i <= tileCount; i++) {
      ctx.beginPath();
      ctx.moveTo(i * gridSize, 0);
      ctx.lineTo(i * gridSize, canvas.height);
      ctx.stroke();
      ctx.beginPath();
      ctx.moveTo(0, i * gridSize);
      ctx.lineTo(canvas.width, i * gridSize);
      ctx.stroke();
    }

    // Draw apples with glow
    apples.forEach(({x, y}) => {
      const cx = x * gridSize + gridSize/2;
      const cy = y * gridSize + gridSize/2;

      ctx.save();
      ctx.translate(cx, cy);
      ctx.shadowColor = '#ff4444';
      ctx.shadowBlur = 18;
      ctx.fillStyle = createAppleGradient();
      ctx.beginPath();
      ctx.ellipse(0, 0, gridSize * 0.4, gridSize * 0.55, Math.PI / 6, 0, 2 * Math.PI);
      ctx.fill();

      // Apple leaf
      ctx.fillStyle = '#228822';
      ctx.beginPath();
      ctx.ellipse(0, -gridSize * 0.3, gridSize * 0.15, gridSize * 0.3, Math.PI / 4, 0, 2 * Math.PI);
      ctx.fill();

      ctx.restore();
    });

    // Draw snake with brighter glow
    ctx.fillStyle = createSnakeGradient();
    for (let i = 0; i < snake.length; i++) {
      const s = snake[i];
      const px = s.x * gridSize;
      const py = s.y * gridSize;

      ctx.save();
      ctx.shadowColor = '#00ffcc';
      ctx.shadowBlur = i === 0 ? 25 : 18; // brighter glow for head
      roundedRect(px + 2, py + 2, gridSize - 4, gridSize - 4, 8);
      ctx.restore();
    }
  }

  // Move snake and handle game logic
  function update() {
    if (gameOver) return;

    // Update velocity from buffered direction
    if ((nextVelocity.x !== -velocity.x || nextVelocity.y !== -velocity.y) &&
        (nextVelocity.x !== velocity.x || nextVelocity.y !== velocity.y)) {
      velocity = nextVelocity;
    }

    // New head position
    const head = { x: snake[0].x + velocity.x, y: snake[0].y + velocity.y };

    // Wrap around screen edges
    if (head.x < 0) head.x = tileCount - 1;
    else if (head.x >= tileCount) head.x = 0;
    if (head.y < 0) head.y = tileCount - 1;
    else if (head.y >= tileCount) head.y = 0;

    // Check collision with self
    for (let part of snake) {
      if (part.x === head.x && part.y === head.y) {
        gameOver = true;
        setTimeout(() => alert(`Game Over! Your score was ${score}. Refresh to play again.`), 100);
        return;
      }
    }

    snake.unshift(head);

    // Check apple eat
    let ateApple = false;
    for (let i = 0; i < apples.length; i++) {
      if (apples[i].x === head.x && apples[i].y === head.y) {
        apples.splice(i, 1);
        score++;
        ateApple = true;
        break;
      }
    }

    if (!ateApple) {
      snake.pop();
    } else {
      spawnApples();
    }
  }

  // Touch controls: swipe detection
  let touchStartX = null;
  let touchStartY = null;
  const minSwipeDistance = 30;

  canvas.addEventListener('touchstart', e => {
    if (e.touches.length === 1) {
      const t = e.touches[0];
      touchStartX = t.clientX;
      touchStartY = t.clientY;
    }
  }, { passive: true });

  canvas.addEventListener('touchmove', e => {
    if (!touchStartX || !touchStartY) return;
    const t = e.touches[0];
    const dx = t.clientX - touchStartX;
    const dy = t.clientY - touchStartY;

    if (Math.abs(dx) > minSwipeDistance || Math.abs(dy) > minSwipeDistance) {
      if (Math.abs(dx) > Math.abs(dy)) {
        // Horizontal swipe
        if (dx > 0) tryChangeDirection('right');
        else tryChangeDirection('left');
      } else {
        // Vertical swipe
        if (dy > 0) tryChangeDirection('down');
        else tryChangeDirection('up');
      }
      touchStartX = null;
      touchStartY = null;
    }
  }, { passive: true });

  // Buttons for touch UI controls
  controls.forEach(btn => {
    btn.addEventListener('click', () => {
      tryChangeDirection(btn.dataset.dir);
    });
  });

  // Change direction helper
  function tryChangeDirection(dir) {
    switch(dir) {
      case 'up':
        if (velocity.y === 0) nextVelocity = { x: 0, y: -1 };
        break;
      case 'down':
        if (velocity.y === 0) nextVelocity = { x: 0, y: 1 };
        break;
      case 'left':
        if (velocity.x === 0) nextVelocity = { x: -1, y: 0 };
        break;
      case 'right':
        if (velocity.x === 0) nextVelocity = { x: 1, y: 0 };
        break;
    }
  }

  // Game loop
  const fps = 10;
  let lastRender = 0;
  function gameLoop(time = 0) {
    if (time - lastRender > 1000 / fps) {
      lastRender = time;
      update();
      draw();
    }
    requestAnimationFrame(gameLoop);
  }

  // Initialize
  spawnApples();
  draw();
  requestAnimationFrame(gameLoop);
})();
</script>

</body>
</html> in one html file