Skip to main content

Step 3: Keyboard Controls

Let's add arrow key controls so players can steer the worm! We'll also prevent the worm from reversing into itself.

Understanding Keyboard Input

p5.js has a built-in function called keyPressed() that runs automatically when any key is pressed. We'll use it to detect arrow keys.

The keyPressed() Function

Add this function to handle keyboard input:

function keyPressed() {
// Right arrow
if (keyCode === RIGHT_ARROW && xSpeed !== -1) {
xSpeed = 1;
ySpeed = 0;
}
// Left arrow
else if (keyCode === LEFT_ARROW && xSpeed !== 1) {
xSpeed = -1;
ySpeed = 0;
}
// Down arrow
else if (keyCode === DOWN_ARROW && ySpeed !== -1) {
xSpeed = 0;
ySpeed = 1;
}
// Up arrow
else if (keyCode === UP_ARROW && ySpeed !== 1) {
xSpeed = 0;
ySpeed = -1;
}
}

Breaking it down:

Checking Which Key Was Pressed

if (keyCode === RIGHT_ARROW)
  • keyCode is a p5.js variable that holds the code of the pressed key
  • RIGHT_ARROW, LEFT_ARROW, etc. are p5.js constants
  • This checks if the right arrow key was pressed

Setting Direction

xSpeed = 1;
ySpeed = 0;
  • For right: move horizontally (x), not vertically (y)
  • Sets the direction for the updateWorm() function to use

Preventing Reverse Movement

if (keyCode === RIGHT_ARROW && xSpeed !== -1)
  • xSpeed !== -1 means "not currently moving left"
  • This prevents the worm from reversing into itself
  • Without this check, the worm could instantly hit its own body!
warning

Why prevent reverse?

If the worm is moving right (xSpeed = 1) and has length > 1:

Head → [2][1][0] ← Tail

If we allowed reversing left (xSpeed = -1), the head would immediately collide with segment [1]:

[2] ← Head turns around
[1] ← COLLISION!
[0]

The game would be instantly over!

Direction Mapping

Here's how each arrow key maps to speeds:

KeyxSpeedySpeedDirection
RIGHT_ARROW10Moving right
LEFT_ARROW-10Moving left
DOWN_ARROW01Moving down
UP_ARROW0-1Moving up

Important: Only ONE speed should be non-zero at a time!

Alternative: WASD Controls

Want to use W/A/S/D keys instead? Here's how:

function keyPressed() {
// D key - Right
if (key === 'd' && xSpeed !== -1) {
xSpeed = 1;
ySpeed = 0;
}
// A key - Left
else if (key === 'a' && xSpeed !== 1) {
xSpeed = -1;
ySpeed = 0;
}
// S key - Down
else if (key === 's' && ySpeed !== -1) {
xSpeed = 0;
ySpeed = 1;
}
// W key - Up
else if (key === 'w' && ySpeed !== 1) {
xSpeed = 0;
ySpeed = -1;
}
}

Difference: Use key instead of keyCode for letter keys!

Complete Code

// Grid settings
let cellSize = 20;
let cols, rows;

// Worm
let worm = [];
let xSpeed = 1;
let ySpeed = 0;

function setup() {
createCanvas(400, 400);
frameRate(10);

// Calculate grid dimensions
cols = width / cellSize;
rows = height / cellSize;

// Initialize worm in the center
let startX = floor(cols / 2);
let startY = floor(rows / 2);
worm.push({x: startX, y: startY});
}

function draw() {
background(50);

// Update worm position
updateWorm();

// Draw grid lines (optional)
stroke(80);
strokeWeight(1);
for (let i = 0; i < cols; i++) {
line(i * cellSize, 0, i * cellSize, height);
}
for (let i = 0; i < rows; i++) {
line(0, i * cellSize, width, i * cellSize);
}

// Draw the worm
noStroke();
fill(0, 255, 0);
for (let i = 0; i < worm.length; i++) {
let segment = worm[i];
rect(segment.x * cellSize, segment.y * cellSize, cellSize, cellSize);
}
}

function updateWorm() {
// Get the current head position
let head = worm[worm.length - 1];

// Calculate new head position
let newHead = {
x: head.x + xSpeed,
y: head.y + ySpeed
};

// Add new head to the front
worm.push(newHead);

// Remove the tail
worm.shift();
}

function keyPressed() {
// Right arrow
if (keyCode === RIGHT_ARROW && xSpeed !== -1) {
xSpeed = 1;
ySpeed = 0;
}
// Left arrow
else if (keyCode === LEFT_ARROW && xSpeed !== 1) {
xSpeed = -1;
ySpeed = 0;
}
// Down arrow
else if (keyCode === DOWN_ARROW && ySpeed !== -1) {
xSpeed = 0;
ySpeed = 1;
}
// Up arrow
else if (keyCode === UP_ARROW && ySpeed !== 1) {
xSpeed = 0;
ySpeed = -1;
}
}

Test It Out!

Run the code and try the controls:

  • ✅ Press arrow keys to change direction
  • ✅ Worm responds immediately to input
  • ✅ Can't reverse direction (try it - it won't work!)
  • ✅ Worm still goes off screen (we'll fix this soon)

Understanding the Logic Flow

When you press RIGHT_ARROW while moving up:

  1. keyPressed() is called
  2. Check: keyCode === RIGHT_ARROW ✓ (true)
  3. Check: xSpeed !== -1 ✓ (true, because xSpeed is 0)
  4. Set: xSpeed = 1, ySpeed = 0
  5. Next frame: updateWorm() uses new speeds
  6. Worm turns right!

When you press LEFT_ARROW while moving right:

  1. keyPressed() is called
  2. Check: keyCode === LEFT_ARROW ✓ (true)
  3. Check: xSpeed !== 1 ✗ (false, because xSpeed IS 1)
  4. Nothing happens - worm keeps moving right
  5. This prevents instant death!

Common p5.js Key Variables

  • keyCode - For special keys (arrows, Enter, Shift, etc.)
  • key - For regular character keys (letters, numbers)

Special key constants:

  • RIGHT_ARROW, LEFT_ARROW, UP_ARROW, DOWN_ARROW
  • ENTER, BACKSPACE, DELETE
  • SHIFT, CONTROL, ALT

What You Learned

  • How to use keyPressed() to detect keyboard input
  • The difference between keyCode and key
  • How to use p5.js arrow key constants
  • Why preventing reverse movement is crucial
  • How to use !== to check inequality
  • Immediate vs delayed response to input