← Back to Blog Apr 23, 2026 · 08:00 AM · 8 min read

How to Make a Pac-Man Game for Beginners

Ever wanted to build your own Pac-Man game? In this tutorial, we will walk through the basics of creating a simple Pac-Man clone using HTML Canvas and vanilla JavaScript. No frameworks needed — just a browser and a text editor.

What You Will Need

Step 1: Set Up the HTML Structure

Start with a simple HTML file that includes a <canvas> element. The canvas is where all the game graphics will be drawn.

Tip: Set your canvas size to match your game grid. A 20x20 tile grid with 20px tiles gives you a 400x400 canvas.
<!DOCTYPE html>
<html>
<head>
  <title>My Pac-Man</title>
  <style>
    body { background: #000; display: flex;
           justify-content: center; align-items: center;
           height: 100vh; margin: 0; }
    canvas { border: 2px solid #333; }
  </style>
</head>
<body>
  <canvas id="game" width="400" height="400"></canvas>
  <script src="pacman.js"></script>
</body>
</html>

Step 2: Define the Game Map

Pac-Man uses a tile-based map. Each tile can be a wall, a dot, a power pellet, or empty space. We represent this as a 2D array where each number means a different tile type.

const TILE_SIZE = 20;
const map = [
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1],
  [1,2,1,1,2,1,1,1,2,1,1,2,1,1,1,2,1,1,2,1],
  [1,3,1,1,2,1,1,1,2,1,1,2,1,1,1,2,1,1,3,1],
  [1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
  // ... more rows
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
];
// 0 = empty, 1 = wall, 2 = dot, 3 = power pellet

Step 3: Draw the Map on Canvas

Use the Canvas 2D API to loop through the map array and draw each tile. Walls are blue rectangles, dots are small white circles, and power pellets are larger flashing circles.

const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');

function drawMap() {
  for (let row = 0; row < map.length; row++) {
    for (let col = 0; col < map[row].length; col++) {
      const x = col * TILE_SIZE;
      const y = row * TILE_SIZE;
      const tile = map[row][col];

      if (tile === 1) {
        ctx.fillStyle = '#2121DE';
        ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
      } else if (tile === 2) {
        ctx.fillStyle = '#fff';
        ctx.beginPath();
        ctx.arc(x + 10, y + 10, 2, 0, Math.PI * 2);
        ctx.fill();
      } else if (tile === 3) {
        ctx.fillStyle = '#fff';
        ctx.beginPath();
        ctx.arc(x + 10, y + 10, 6, 0, Math.PI * 2);
        ctx.fill();
      }
    }
  }
}

Step 4: Create the Pac-Man Character

Pac-Man is a yellow circle with an animated mouth. We track his position, direction, and use a simple mouth animation that opens and closes.

let pacman = { x: 1, y: 1, dir: 0, mouth: 0.2 };
let mouthOpening = true;

function drawPacman() {
  const cx = pacman.x * TILE_SIZE + TILE_SIZE / 2;
  const cy = pacman.y * TILE_SIZE + TILE_SIZE / 2;
  const angle = pacman.dir * Math.PI / 2;

  ctx.fillStyle = '#FFFF00';
  ctx.beginPath();
  ctx.arc(cx, cy, TILE_SIZE / 2 - 2,
    angle + pacman.mouth,
    angle + Math.PI * 2 - pacman.mouth);
  ctx.lineTo(cx, cy);
  ctx.fill();

  // Animate mouth
  if (mouthOpening) {
    pacman.mouth += 0.04;
    if (pacman.mouth > 0.4) mouthOpening = false;
  } else {
    pacman.mouth -= 0.04;
    if (pacman.mouth < 0.05) mouthOpening = true;
  }
}

Step 5: Handle Keyboard Input

Listen for arrow key presses to change Pac-Man's direction. We store the desired direction and only apply it when the move is valid (not into a wall).

let nextDir = 0;

document.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowRight') nextDir = 0;
  if (e.key === 'ArrowDown')  nextDir = 1;
  if (e.key === 'ArrowLeft')  nextDir = 2;
  if (e.key === 'ArrowUp')    nextDir = 3;
});

function movePacman() {
  const dirs = [{x:1,y:0},{x:0,y:1},{x:-1,y:0},{x:0,y:-1}];
  const d = dirs[nextDir];
  const nx = pacman.x + d.x;
  const ny = pacman.y + d.y;

  if (map[ny] && map[ny][nx] !== 1) {
    pacman.x = nx;
    pacman.y = ny;
    pacman.dir = nextDir;
    if (map[ny][nx] === 2 || map[ny][nx] === 3) {
      map[ny][nx] = 0; // eat the dot
    }
  }
}

Step 6: Add the Game Loop

The game loop ties everything together. It clears the canvas, draws the map, moves Pac-Man, and draws him — all at a steady frame rate.

function gameLoop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#000';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  drawMap();
  movePacman();
  drawPacman();
}

setInterval(gameLoop, 150); // ~7 FPS for tile-based movement

Step 7: Add Simple Ghost AI

Ghosts move randomly through the maze. Each frame, a ghost picks a random valid direction. This is the simplest AI — you can make it smarter later by chasing Pac-Man's position.

let ghosts = [
  { x: 9, y: 9, dir: 0, color: '#FF0000' },
  { x: 10, y: 9, dir: 2, color: '#FFB8FF' },
];

function moveGhost(ghost) {
  const dirs = [{x:1,y:0},{x:0,y:1},{x:-1,y:0},{x:0,y:-1}];
  // Try random direction, fallback to current
  const tryDirs = [0,1,2,3].sort(() => Math.random() - 0.5);
  for (const d of tryDirs) {
    const nx = ghost.x + dirs[d].x;
    const ny = ghost.y + dirs[d].y;
    if (map[ny] && map[ny][nx] !== 1) {
      ghost.x = nx;
      ghost.y = ny;
      ghost.dir = d;
      return;
    }
  }
}

What's Next?

This gives you a basic working Pac-Man. From here you can add:

Want to see a complete Pac-Man? Play our version and study the patterns — it might give you ideas for your own game!

Ready for the next step? Continue to Part 2: Score Tracking where we add dot counting, score display, high scores, and a win condition.

More Articles

🏆
Tutorial Pac-Man Score Tracking: Count Dots and Display the Score Tutorial · Apr 23, 2026 · 09:00 AM
👻
Tutorial Pac-Man Ghost Chase AI: Make Ghosts Hunt the Player Tutorial · Apr 23, 2026 · 10:00 AM
💊
Tutorial Pac-Man Power Pellet Mode: Ghosts Turn Blue Tutorial · Apr 23, 2026 · 11:00 AM
← Back to Blog