JavaScript Navigator getGamepads() Method

Beginner
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs

What You’ll Learn

navigator.getGamepads() is the snapshot entry point to the Gamepad API. Learn the returned array (including null slots), gamepadconnected, reading buttons and axes, five examples, and try-it labs.

01

Kind

Method

02

Returns

Gamepad[] (may include null)

03

Status

Baseline Widely available

04

API family

Gamepad API

05

Events

gamepadconnected

06

Input

buttons · axes

Introduction

Controllers, joysticks, and many gamepads can talk to the browser through the Gamepad API. navigator.getGamepads() returns the current snapshot of connected pads.

It is not a live stream by itself. Games usually listen for connect/disconnect events, then poll getGamepads() inside a requestAnimationFrame loop to read buttons and sticks.

💡
Press a button first

On many browsers the pad list stays empty until the user presses a button on the controller. That is normal — listen for gamepadconnected.

Understanding the getGamepads() Method

  • No parameters — call navigator.getGamepads().
  • Returns an array — each entry is a Gamepad or null.
  • Stable indexesnull holes keep other pads at the same index after disconnect.
  • Useful fieldsid, index, buttons, axes, connected, mapping, timestamp.
  • Connect eventwindow gamepadconnected (and gamepaddisconnected).
  • Permissions Policy — may throw SecurityError if blocked.

📝 Syntax

General form of the method:

JavaScript
navigator.getGamepads()

Parameters

  • None.

Return value

  • An Array of Gamepad objects (may be empty; entries may be null).

Common patterns

JavaScript
// MDN: use connect event, then read by index
window.addEventListener("gamepadconnected", (e) => {
  const gp = navigator.getGamepads()[e.gamepad.index];
  console.log(
    `Gamepad connected at index ${gp.index}: ${gp.id} with ${gp.buttons.length} buttons, ${gp.axes.length} axes.`
  );
});

// Skip null slots when listing pads
function listConnectedPads() {
  return Array.from(navigator.getGamepads()).filter(Boolean);
}

⚡ Quick Reference

GoalCode
Feature detecttypeof navigator.getGamepads === "function"
Snapshotnavigator.getGamepads()
Non-null padsArray.from(navigator.getGamepads()).filter(Boolean)
On connectwindow.addEventListener("gamepadconnected", …)
First button pressed?gp.buttons[0].pressed
Left stick Xgp.axes[0] (typical)

🔍 At a Glance

Four facts to remember about navigator.getGamepads().

Returns
Array

Gamepad or null

Baseline
Widely

Since Mar 2017

Wake-up
Press button

Then connected event

Update
Poll rAF

Each animation frame

📋 Events vs Polling

Connect / disconnect eventsgetGamepads() polling
PurposeKnow when pads appear / leaveRead live button & axis state
How oftenOn plug / unplug (after gesture)Every frame in a game loop
Typical useLog pad id, enable controls UIMove character, fire, aim
Together?Yes — events for lifecycle, polling for input

Examples Gallery

Examples follow MDN Gamepad API patterns. Prefer Try It Yourself — without a controller (and a button press) the list is often empty.

📚 Getting Started

Detect the API and list non-null gamepads.

Example 1 — Feature Detection

Check whether getGamepads exists before using the Gamepad API.

JavaScript
const lines = [
  "getGamepads: " +
    (typeof navigator.getGamepads === "function" ? "available" : "missing"),
  "slots: " +
    (typeof navigator.getGamepads === "function"
      ? navigator.getGamepads().length
      : "n/a")
];
console.log(lines.join("\n"));
Try It Yourself

How It Works

Baseline support is excellent; empty slots usually mean “no activated pad yet.”

Example 2 — List Connected Pads (Skip null)

Filter out empty index holes before displaying controllers.

JavaScript
function listConnectedPads() {
  if (!navigator.getGamepads) return ["getGamepads not supported"];
  const pads = Array.from(navigator.getGamepads()).filter(Boolean);
  if (!pads.length) return ["No active gamepads (press a button on your controller)"];
  return pads.map(
    (gp) =>
      "#" + gp.index + " " + gp.id +
      " | buttons=" + gp.buttons.length +
      " axes=" + gp.axes.length
  );
}

console.log(listConnectedPads().join("\n"));
Try It Yourself

How It Works

filter(Boolean) drops null slots so UI lists stay clean.

📈 Practical Patterns

Handle connect events, read buttons, and poll in a game loop.

Example 3 — gamepadconnected (MDN)

MDN pattern: on connect, re-read the pad from getGamepads() by index.

JavaScript
window.addEventListener("gamepadconnected", (e) => {
  const gp = navigator.getGamepads()[e.gamepad.index];
  console.log(
    "Gamepad connected at index " + gp.index + ": " + gp.id +
    " with " + gp.buttons.length + " buttons, " + gp.axes.length + " axes."
  );
});

console.log("Listening for gamepadconnected — press a button on your pad");
Try It Yourself

How It Works

Also listen for gamepaddisconnected to clear UI when a pad is unplugged.

Example 4 — Read Buttons & Axes

Inspect the first connected pad’s primary button and stick axes.

JavaScript
function describeFirstPad() {
  if (!navigator.getGamepads) return "getGamepads not supported";
  const gp = Array.from(navigator.getGamepads()).find(Boolean);
  if (!gp) return "No active gamepad";
  const btn0 = gp.buttons[0];
  return [
    "id: " + gp.id,
    "button0.pressed: " + (btn0 ? btn0.pressed : "n/a"),
    "button0.value: " + (btn0 ? btn0.value : "n/a"),
    "axes: [" + gp.axes.map((a) => a.toFixed(2)).join(", ") + "]"
  ].join("\n");
}

console.log(describeFirstPad());
Try It Yourself

How It Works

Analog triggers often use button.value (0–1), not only pressed.

Example 5 — Simple Poll Loop Sketch

Beginner game-loop pattern: poll pads each animation frame.

JavaScript
let frames = 0;

function pollGamepads() {
  frames += 1;
  const pads = navigator.getGamepads
    ? Array.from(navigator.getGamepads()).filter(Boolean)
    : [];
  if (frames <= 3) {
    console.log("frame " + frames + " pads=" + pads.length);
  }
  if (frames < 3) {
    requestAnimationFrame(pollGamepads);
  } else {
    console.log("Stopped demo poll after 3 frames");
  }
}

requestAnimationFrame(pollGamepads);
Try It Yourself

How It Works

In a real game, keep polling while the scene is active and read axes/buttons each frame.

🚀 Common Use Cases

  • Browser games — move characters with sticks and face buttons.
  • Emulators / retro players — map Gamepad buttons to virtual keys.
  • Accessibility remotes — alternate input when a controller is preferred.
  • Controller test pages — visualize axes and button presses for debugging.
  • Local multiplayer — track multiple pads by stable index.

🧠 How navigator.getGamepads() Works

1

User activates a pad

Press a button; the browser fires gamepadconnected.

Gesture
2

Call getGamepads()

Read the array and pick the pad by index (skip null).

Snapshot
3

Poll each frame

Inside requestAnimationFrame, refresh buttons and axes.

Loop
4

Drive your game UI

Apply movement, actions, and menus from the latest snapshot.

📝 Notes

  • Baseline Widely available (MDN, since March 2017).
  • Not Deprecated, Experimental, or Non-standard — no status banner required.
  • Array entries may be null — always guard when looping.
  • Pads often appear only after a controller button press.
  • Related: getInstalledRelatedApps(), getBattery(), xr, Window.

Universal Browser Support

navigator.getGamepads() is Baseline Widely available across modern browsers (MDN: since March 2017). Controllers still usually need a user button press before they appear in the array. Always skip null slots and poll during gameplay.

Baseline · Widely available

Navigator.getGamepads()

Array snapshot of Gamepad objects — buttons, axes, and stable indexes.

Universal Widely available
Google Chrome Gamepad API · press button to activate
Full support
Mozilla Firefox Gamepad API · modern versions
Full support
Microsoft Edge Gamepad API · Chromium
Full support
Apple Safari Gamepad API · modern versions
Full support
Opera Gamepad API · modern versions
Full support
Internet Explorer No modern Gamepad getGamepads path
Unavailable
getGamepads() Excellent

Bottom line: Listen for gamepadconnected, call getGamepads() for snapshots, filter nulls, and poll buttons/axes each animation frame.

Conclusion

navigator.getGamepads() is the Baseline way to snapshot connected controllers. Wait for a button press / gamepadconnected, skip null slots, then poll buttons and axes in your game loop.

Continue with getInstalledRelatedApps(), getBattery(), or the JavaScript hub.

💡 Best Practices

✅ Do

  • Listen for gamepadconnected / gamepaddisconnected
  • Skip null entries in the returned array
  • Poll getGamepads() each animation frame during play
  • Prompt users to press a button to activate the pad
  • Handle Permissions Policy SecurityError

❌ Don’t

  • Assume pads appear without a user gesture
  • Forget that disconnect can leave null holes
  • Cache one Gamepad object forever without re-polling
  • Require a controller when keyboard/mouse works
  • Ignore deadzones on analog axes

Key Takeaways

Knowledge Unlocked

Five things to remember about getGamepads()

Snapshot controllers — connect event, then poll each frame.

5
Core concepts
02

Baseline

Widely available

Since 2017
🚫 03

Holes

null slots OK

Indexes
🖱 04

Activate

press a button

Connect
🎯 05

Update

poll each frame

rAF

❓ Frequently Asked Questions

It returns an array of Gamepad objects for controllers connected to the device. Some slots may be null if a pad disconnected, so remaining pads keep the same index.
No. MDN marks it Baseline Widely available (since March 2017). It is not Deprecated, Experimental, or Non-standard — no status banner is required.
Many browsers only expose gamepads after the user presses a button on the controller (privacy / gesture). Listen for gamepadconnected, then call getGamepads().
If a gamepad disconnects mid-session, its slot may become null so other pads keep stable indexes. Always skip null entries when looping.
Use gamepad.buttons (pressed / value) and gamepad.axes (usually -1 to 1). Poll getGamepads() each animation frame for continuous input.
Yes. A SecurityError can be thrown if use is blocked by a Permissions Policy.
Did you know?

A Gamepad object from an earlier poll can go stale. Always call navigator.getGamepads() again (or re-read the same index) when you need fresh button and axis values.

Explore getInstalledRelatedApps() Next

Hide install banners when related apps are already installed.

getInstalledRelatedApps() →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

8 people found this page helpful