Testing

Gamepad API: Browser Support, Features, Known Issues

The Gamepad API works in Chrome 21+, Edge 12+, Firefox 29+, Safari 10.1+ on macOS, Safari 10.3+ on iOS, and Samsung Internet 4+. Learn the features and code.

Author

Prince Dewani

May 1, 2026

The Gamepad API is a W3C JavaScript interface that lets web pages read button presses and joystick movement from connected gamepads through navigator.getGamepads and connection events. It works in Chrome 21+, Edge 12+, Firefox 29+, Opera 24+, Safari 10.1+ on macOS, Safari 10.3+ on iOS, and Samsung Internet 4+, while Internet Explorer never added support.

This guide covers what the Gamepad API is, which browsers support it, key features, how to connect to a gamepad, use cases, and known issues.

What is the Gamepad API?

The Gamepad API is a W3C web standard that lets a web page read input from USB and Bluetooth game controllers without a plugin. The browser exposes connected devices through navigator.getGamepads, the Gamepad object, and the gamepadconnected and gamepaddisconnected events on the window.

Which browsers does the Gamepad API support?

The Gamepad API ships in every modern browser, with global support near 96% across desktop and mobile. Internet Explorer and the legacy stock Android Browser are the only mainstream targets without support.

Loading browser compatibility data...

Gamepad API compatibility in Chrome

Chrome supports the Gamepad API from Chrome 21 on Windows, macOS, Linux, ChromeOS, and Android. Chrome ships the standard Xbox-style mapping and the GamepadHapticActuator for dual-rumble feedback from Chrome 89 on. Modern Chrome restricts navigator.getGamepads to secure (HTTPS) contexts and waits for a user gesture before exposing connected devices.

Gamepad API compatibility in Edge

Microsoft Edge supports the Gamepad API from Edge 12 on Windows. Pre-Chromium EdgeHTML 12 to 18 used the Microsoft implementation, and Chromium-based Edge 79 and later track the upstream Chromium stack. Behavior matches Chrome on every Edge release still in support.

Gamepad API compatibility in Firefox

Firefox supports the Gamepad API from Firefox 29 on Windows, macOS, Linux, and Android. Firefox 24 to 28 had it disabled by default behind the dom.gamepad.enabled flag, and Firefox 23 and earlier did not support it. Firefox only exposes connected gamepads after the user presses a button on a controller while the page is visible.

Gamepad API compatibility in Safari

Safari supports the Gamepad API from Safari 10.1 on macOS and from Safari 10.3 on iOS and iPadOS. Apple ships the standard Xbox-style mapping on every supported version and added GamepadHapticActuator support for dual-rumble in newer Safari builds. Safari 10 and earlier did not support the Gamepad API.

Gamepad API compatibility in Opera

Opera supports the Gamepad API from Opera 24 on Windows, macOS, and Linux. Opera Mobile 80 and later expose the same Chromium-backed implementation on Android phones and tablets. Opera 9 to 23 did not support the Gamepad API at all.

Gamepad API compatibility in Samsung Internet

Samsung Internet supports the Gamepad API from Samsung Internet 4 on Galaxy phones and tablets. The browser tracks the Chromium gamepad stack, so it ships the standard mapping and connection events on every current Galaxy device. Samsung Internet 1 to 3 did not support the Gamepad API.

Gamepad API compatibility in Android Browser

The legacy stock Android Browser based on the old WebView did not support the Gamepad API at all. Modern Android devices use Chrome for Android, Samsung Internet, or Firefox for Android instead, all three of which expose the API. Test gamepad-driven web games on Chrome for Android and Samsung Internet, not on the legacy WebView.

Gamepad API compatibility in Internet Explorer

Internet Explorer does not support the Gamepad API in any version. Internet Explorer 5.5 through 11 never shipped navigator.getGamepads, and Microsoft has retired Internet Explorer. Move gamepad-driven workloads to Chromium-based Edge or any other modern browser.

Note

Note: The Gamepad API behaves differently across browsers, controllers, and operating systems. Test it on real browsers and OS with TestMu AI. Try TestMu AI free!

What are the key features of the Gamepad API?

The Gamepad API exposes connected controllers through one navigator method, two window events, and a small set of objects. Most browsers ship the same standard Xbox-style mapping, while extensions add haptic feedback and touch surfaces.

  • navigator.getGamepads(): Returns an array of Gamepad objects for every controller the browser has detected after a user gesture. The slot index stays stable for the life of the page, so you can match a controller to a player.
  • gamepadconnected and gamepaddisconnected events: Fire on the window object whenever a controller is plugged in or pulled out. The handler receives a GamepadEvent with the new Gamepad object on event.gamepad.
  • GamepadButton interface: Reports each button as a record with pressed, touched, and value (0.0 to 1.0) fields. The value field captures pressure for analog triggers like Xbox LT and RT, while pressed gives a clean digital signal.
  • Standard Xbox-style mapping: Buttons 0 to 16 cover the right cluster (0 to 3), front triggers (4 to 7), Select and Start (8 and 9), stick press (10 and 11), D-pad (12 to 15), and the home button (16). Axes 0 to 3 cover the left and right analog sticks normalized to -1 to 1.
  • GamepadHapticActuator: Provides playEffect and reset for dual-rumble vibration. Chrome 89+, Edge 89+, and recent Firefox and Safari builds support the actuator; older browsers expose only the basic Gamepad object without rumble.

How do you connect to a gamepad with the Gamepad API?

You attach gamepadconnected and gamepaddisconnected listeners on the window, store the controller index when the event fires, and poll navigator.getGamepads inside requestAnimationFrame to read button and axis state every frame.

  • Listen for gamepadconnected: Call window.addEventListener("gamepadconnected", handler) so the browser tells the page when a controller is detected. The handler receives a GamepadEvent with the live Gamepad object on event.gamepad.
  • Save the gamepad index: Read event.gamepad.index inside the handler and stash it on a module-level variable. The index stays stable across frames, so future polls pick the same controller.
  • Wait for a user gesture: Modern Chrome and Firefox return an empty array from navigator.getGamepads until the user presses a button on the controller while the tab is focused. Prompt the user to press any button before reading state.
  • Poll inside requestAnimationFrame: Call navigator.getGamepads on every frame, look up the saved index, and read the buttons and axes arrays. Polling beats events for analog sticks because the values change continuously.
  • Tear down on disconnect: Listen for gamepaddisconnected and clear the saved index when the user pulls the controller. This stops the polling loop from reading a stale or null gamepad slot.

Paste this snippet into the DevTools console of a page on HTTPS, then press any button on a connected controller to confirm support and watch button and axis values stream in.

// Paste this into the DevTools console of a page on HTTPS, then press any button on a connected controller.
let activeGamepadIndex = null;

window.addEventListener("gamepadconnected", (event) => {
  activeGamepadIndex = event.gamepad.index;
  console.log(
    "Gamepad connected at slot",
    event.gamepad.index,
    event.gamepad.id,
    event.gamepad.buttons.length,
    "buttons,",
    event.gamepad.axes.length,
    "axes."
  );
});

window.addEventListener("gamepaddisconnected", (event) => {
  if (event.gamepad.index === activeGamepadIndex) activeGamepadIndex = null;
  console.log("Gamepad disconnected from slot", event.gamepad.index);
});

function pollLoop() {
  if (activeGamepadIndex !== null) {
    const pad = navigator.getGamepads()[activeGamepadIndex];
    if (pad) {
      const pressed = pad.buttons.findIndex((button) => button.pressed);
      if (pressed !== -1) {
        console.log(
          "Button",
          pressed,
          "pressed; left stick at",
          pad.axes[0].toFixed(2),
          pad.axes[1].toFixed(2)
        );
      }
    }
  }
  requestAnimationFrame(pollLoop);
}

requestAnimationFrame(pollLoop);

If the page never receives gamepadconnected, the most common cause is a missing user gesture on Chrome or a non-secure (HTTP) context that newer browsers block. Reload on HTTPS and ask the user to press any button.

What are the use cases of the Gamepad API?

The Gamepad API lets browser-based games, accessibility tools, and creative apps read controller input without a plugin. Any product whose users already own a console-grade controller wins by accepting it on the web.

  • Browser-based games: Cloud gaming services like Xbox Cloud Gaming and GeForce NOW read controller input through the Gamepad API. Independent web games on itch.io, Construct, and Phaser also accept Xbox, PlayStation, and Switch Pro controllers without any user setup.
  • Accessibility navigation: Sites that need an alternative to keyboard and mouse can map gamepad axes and buttons to scroll, click, and form-fill actions. The Gamepad Navigator extension and similar tools rely on the API to drive the page from a controller.
  • Creative tools and demos: WebGL and WebGPU demos, three.js scenes, and CodePen experiments use gamepad axes to fly cameras, drive vehicles, and control parameters that would be awkward with a mouse. The standard mapping makes one set of code work across every supported controller.
  • Live performance and music apps: Live coding environments and music tools map analog triggers to volume, pitch, or filter parameters. The 0.0 to 1.0 value range on each GamepadButton is enough resolution for expressive control.
  • Remote-control dashboards: Browser dashboards for drones, robots, and 3D printers map sticks and buttons to motion commands. The Gamepad API plus WebSockets or WebRTC carries inputs from the browser to the device with sub-100-millisecond latency.
...

What are the known issues with the Gamepad API?

The Gamepad API works on every modern browser, but the rough edges cluster around platform-specific button maps, stricter security gates, and inconsistent haptic support.

  • Standard mapping is not guaranteed: When gamepad.mapping is the empty string, the browser exposes the raw HID descriptor and your button indices are anyone's guess. Read pad.mapping === "standard" before assuming buttons[0] is the south face button.
  • Secure context and user gesture: Chrome and Firefox return an empty list from navigator.getGamepads on plain HTTP and on pages that have not yet seen a user gesture. Test on https:// and prompt the user to press any button before polling.
  • Firefox only exposes input on focus: Firefox blocks gamepad polling in background tabs and unfocused windows to limit fingerprinting. A game that pauses when its tab loses focus will see flat 0 values on every axis.
  • Patchy haptic support: GamepadHapticActuator.playEffect ships in current Chrome, Edge, and recent Firefox and Safari, but older Safari and Samsung Internet builds only expose the basic Gamepad object. Wrap rumble calls in a feature check and degrade gracefully.
  • Controller IDs differ across OS: The same Xbox controller reports different gamepad.id strings on Windows ("Xbox 360 Controller (XInput STANDARD GAMEPAD)") and macOS ("Xbox Wireless Controller"). Match controllers by mapping and button count, not by id substring.
  • No remapping API: The Gamepad API does not let pages remap buttons system-wide. If a player wants Y and B swapped, the page has to ship its own input layer because the browser does not write to the OS HID descriptor.

In my experience, the bug that bites teams hardest is the secure-context gate on Chrome. A controller that worked perfectly on localhost goes silent on a staging URL served over HTTP because navigator.getGamepads quietly returns an empty array. Move staging to HTTPS first, then debug.

...

Citations

All Gamepad API version numbers and platform notes in this guide come from these primary sources:

Author

Prince Dewani is a Community Contributor at TestMu AI, where he manages content strategies around software testing, QA, and test automation. He is certified in Selenium, Cypress, Playwright, Appium, Automation Testing, and KaneAI. Prince has also presented academic research at the international conference PBCON-01. He further specializes in on-page SEO, bridging marketing with core testing technologies. On LinkedIn, he is followed by 4,300+ QA engineers, developers, DevOps experts, tech leaders, and AI-focused practitioners in the global testing community.

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free

Frequently asked questions

Did you find this page helpful?

More Related Hubs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests