Back

HTML5 Canvas: Handling Player Input (Keyboard & Mouse)

7 min read

HTML5 Canvas: Handling Player Input

A game without player input is just an animation. To make it a true game, we need to allow the player to control elements on the screen. Let's learn to capture keyboard and mouse input effectively!

1. The Right Way to Handle Keyboard Input

If you simply attach an event listener to a button press and move the player directly inside that listener, the movement will feel choppy and clunky.

The industry-standard way to do this is to keep track of which keys are currently being held down. We do this by storing the state in an object, and then applying movement inside our requestAnimationFrame Game Loop.

Step 1: Create a State Object

const keys = {
  ArrowUp: false,
  ArrowDown: false,
  ArrowLeft: false,
  ArrowRight: false
};

Step 2: Listeners to Update State

window.addEventListener('keydown', (e) => {
  if (keys.hasOwnProperty(e.code)) {
    keys[e.code] = true; // Key is being pressed
  }
});

window.addEventListener('keyup', (e) => {
  if (keys.hasOwnProperty(e.code)) {
    keys[e.code] = false; // Key was released
  }
});

Step 3: Apply to Game Loop

Now, inside the same Game Loop we learned about previously!

let player = { x: 100, y: 100, size: 30, speed: 5 };

function update() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Smooth movement checked every single frame!
  if (keys.ArrowUp) player.y -= player.speed;
  if (keys.ArrowDown) player.y += player.speed;
  if (keys.ArrowLeft) player.x -= player.speed;
  if (keys.ArrowRight) player.x += player.speed;

  // Draw Player
  ctx.fillStyle = 'blue';
  ctx.fillRect(player.x, player.y, player.size, player.size);

  requestAnimationFrame(update);
}

2. Tracking the Mouse

Mouse tracking is incredibly useful for shooting games, drag-and-drop puzzles, or custom cursors. Because the standard e.clientX gives the position based on the whole browser window, we must subtract the canvas's padding/margin bounds to get the exact X and Y on our game grid.

let mouse = { x: 0, y: 0 };

canvas.addEventListener('mousemove', (e) => {
  // Get the size and position of the canvas on the webpage
  const rect = canvas.getBoundingClientRect(); 
  
  // Calculate exact coordinates relative to canvas
  mouse.x = e.clientX - rect.left;
  mouse.y = e.clientY - rect.top;
});

// Want a click event?
canvas.addEventListener('click', (e) => {
  console.log(`Pew pew! Gun fired at X: ${mouse.x}, Y: ${mouse.y}`);
});

Now you can build a game loop that constantly draws an crosshair image exactly at mouse.x and mouse.y!