Phaser Gamepad Controls: A 2026 Beginner Guide


Primary keyword: how to add gamepad controls in Phaser for beginners.

Adding a controller to a Phaser game is a small input-system upgrade with a big payoff: players can move, jump, and aim without touching the keyboard. This guide uses Phaser’s built-in GamepadPlugin, a simple polling loop, and a keyboard fallback. The code targets Phaser 3-style Scenes and avoids an external controller library.

How This Guide Was Built

This guide is based on the official Phaser GamepadPlugin documentation, the MDN Gamepad API guide, and community reports about browser behavior. I verified the Phaser manager, connection event, pad references, buttons, and axes against those documents. I did not test physical hardware or paid tools. Last verified: August 2026.

What do I need before adding gamepad input?

You need a Phaser 3 game, a browser with Gamepad API support, and a controller; serve the page from localhost or HTTPS when possible. The MDN Gamepad API guide explains that browsers expose connected controllers to the focused page, sometimes only after player interaction.

You can use an existing project, such as our Phaser platformer tutorial, or start with a minimal Scene. Keep keyboard input enabled while you develop: it gives you a reliable way to check whether a movement bug belongs to the game logic or the controller connection.

How do I add gamepad controls in Phaser for beginners?

To add gamepad controls in Phaser, enable the input manager, listen for a controller connection, then read the active pad’s button and axis state in update(). Phaser’s GamepadPlugin documentation exposes the manager through this.input.gamepad and provides references for connected pads.

Start with an explicit configuration. Phaser creates the Scene input system for you, while gamepad: true makes the intent clear:

const config = {
  type: Phaser.AUTO,
  width: 800,
  height: 450,
  scene: PlayScene,
  input: {
    gamepad: true
  }
};

new Phaser.Game(config);

Inside the Scene, listen for Phaser’s connected event. The browser may wait for a button press before exposing a controller, so do not treat an empty pad reference as a fatal error:

create() {
  this.pad = this.input.gamepad.pad1;

  this.input.gamepad.once('connected', (pad) => {
    this.pad = pad;
    console.log('Controller connected:', pad.id);
  });
}

The plugin exposes pad1 through pad4, so a single-player game can use pad1 and a local multiplayer game can assign one reference per player.

How do I read gamepad buttons and axes in Phaser?

Read pad.buttons and pad.axes during the Scene’s update() loop, checking that a pad exists first. The Phaser Gamepad API reference documents these Gamepad properties, while MDN’s usage guide recommends reading current state during the animation loop.

A button has a pressed state, and an axis supplies a numeric stick position. The exact button and axis order can vary by controller, so log the values while building your mapping:

update() {
  const pad = this.pad || this.input.gamepad.pad1;
  if (!pad) return;

  const horizontal = pad.axes[0] ? pad.axes[0].getValue() : 0;
  const jumpPressed = pad.buttons[0] && pad.buttons[0].pressed;

  this.player.setVelocityX(horizontal * 260);
  if (jumpPressed && this.player.body.blocked.down) {
    this.player.setVelocityY(-420);
  }
}

For a button that should fire once rather than every frame, remember its previous state:

const pressed = Boolean(pad.buttons[0]?.pressed);
if (pressed && !this.wasJumpPressed) this.jump();
this.wasJumpPressed = pressed;

This edge detection prevents a held button from repeatedly triggering a one-time action.

How should I handle analog-stick dead zones?

A dead zone converts tiny analog values to zero, preventing a character from drifting when the stick appears centered. Apply the filter before multiplying the axis value, and keep the threshold easy to tune. This is game logic built on the axis values described in the MDN Gamepad API documentation.

function withDeadZone(value, threshold = 0.2) {
  return Math.abs(value) < threshold ? 0 : value;
}

const rawX = pad.axes[0] ? pad.axes[0].getValue() : 0;
const moveX = withDeadZone(rawX);
this.player.setVelocityX(moveX * 260);

Start with a modest threshold and adjust it after observing the controller. Do not assume every device uses the same axis arrangement; a small debug label showing the current axis values makes remapping much easier.

How do I keep keyboard controls as a fallback?

Keep keyboard controls alongside gamepad polling so the game remains playable when no controller is connected. Phaser’s keyboard input and GamepadPlugin can be read in the same update() method, letting either device produce a movement value without duplicating the player physics code.

create() {
  this.cursors = this.input.keyboard.createCursorKeys();
}

update() {
  const pad = this.pad || this.input.gamepad.pad1;
  let moveX = 0;

  if (pad?.axes[0]) moveX = withDeadZone(pad.axes[0].getValue());
  if (this.cursors.left.isDown) moveX = -1;
  if (this.cursors.right.isDown) moveX = 1;

  this.player.setVelocityX(moveX * 260);
}

The keyboard override is useful during development and gives players an accessible alternative. Apply the resulting movement value to one shared player function rather than maintaining separate movement implementations.

What happens when a gamepad disconnects?

A disconnected controller should clear your stored reference and return the game to a safe keyboard-only state. Phaser exposes a disconnected event on its gamepad manager; the browser-level MDN documentation describes the corresponding gamepaddisconnected event.

this.input.gamepad.on('disconnected', (pad) => {
  if (this.pad === pad) this.pad = null;
  this.statusText.setText('Controller disconnected — keyboard still works');
});

You can also check this.input.gamepad.pad1 again in update() instead of trusting a long-lived reference. If a controller reconnects, the connected listener can replace the reference and update your on-screen control prompt.

Common mistakes with Phaser gamepad controls

The common mistakes are reading pad.buttons before checking pad, testing only the connection event, forgetting browser security requirements, and hard-coding a button layout without inspecting a real device. The Phaser docs specifically note that a button press may be needed before access is granted.

Troubleshoot in this order:

  1. Focus the game page and press a controller button.
  2. Use localhost or HTTPS rather than opening an untrusted page context.
  3. Log pad.id, pad.buttons.length, and pad.axes.length after connection.
  4. Confirm your game checks pad before reading it.
  5. Keep keyboard movement active while mapping the controller.

FAQ

Why is my gamepad not detected in Phaser?

A browser may not expose an already-connected controller until the focused page receives a button press or axis movement. Press a button after the game loads, confirm the page is served from localhost or HTTPS, and inspect the connected callback. The Phaser GamepadPlugin docs describe this activation behavior.

Can Phaser support more than one gamepad?

Yes. Phaser’s GamepadPlugin provides pad1, pad2, pad3, and pad4 references for connected controllers, according to the official Phaser API reference. Assign each reference to a player, but inspect each device’s button and axis layout instead of assuming all controllers report identical mappings.

How do I stop a character drifting with a controller?

Filter the horizontal axis through a dead zone before applying velocity. If the absolute value is below a small threshold, use zero; otherwise keep the original value. This removes minor stick noise while preserving deliberate movement. Tune the threshold with the controller you expect players to use, then retain keyboard fallback.

Where to go next

Add this input layer to our Phaser Pong tutorial or the platformer above, then compare the result with other AI game frameworks. When the controls feel good, browse published browser games for ideas about menus, input prompts, and accessible control schemes.

HERO_IMAGE_PROMPT: Dark navy game development desk with a modern gamepad beside a laptop showing Phaser JavaScript input code, glowing orange, cyan, and purple neon accents, beginner-friendly editorial illustration, no text or logos.