The HTML5 Gamepad API is one of the more exciting HTML5 APIs in my opinion. It allows a website to pretty easily take input from a game controller that is connected to the user’s machine. The API supports hundreds of game controllers, both wireless and wired, including Xbox One controllers and PS4 controllers to name a few.
Before we begin, note that the gamepad API may not detect a gamepad until you press a button or move a stick on the controller. So, make sure to press some of the buttons when you test any sites or programs that use the Gamepad API.
Note: controllers and other game inputs are referred to as ‘gamepads’ throughout this article and in the gamepad API documentation.
Checking if Your Gamepad is Connected and Troubleshooting Potential Issues
To check if your gamepad is successfully connected, run navigator.getGamepads()
in the JavaScript console and check if the result has a Gamepad object, and is not an array of null elements.
If your gamepad isn’t working with the API, here are a few things to try:
- check if the device is connected to your machine via bluetooth, USB, or other method
- try restarting your computer or web browser
- try pressing some of the buttons or moving one of the sticks on the controller so that it is detected
- try closing any other games or apps that are using the gamepad
Get a List of Connected Gamepads
The Gamepad API allows up to four gamepads to be connected at once.
To get an array of connected gamepads, use the navigator.getGamepads()
method. The array is always of length four, where unused gamepad slots are null
. The element(s) of connected gamepad(s) are Gamepad
object(s). Here’s an example value of the navigator.getGamepads()
method:
console.log(navigator.getGamepads());
// --> [Gamepad Object, null, null, null]
// Here, only one gamepad is connected, and the other three gamepad slots are null.
Before trying to use a the functionality of the Gamepad API, make sure a gamepad is connected using the instructions in the previous section.
The Gamepad
Object
The Gamepad
object includes two important properties that are available in the vast majority of all gamepads and controllers: buttons
and axes
.
axes
is an array of length four which represents the position of the left and right sticks in the gamepad. The first two elements in axes
are the (x, y) coordinates of the position of the left stick, whereas the third and fourth elements in axes
are the (x, y) coordinates of the position of the right stick. (x, y) values are numbers between -1 and 1 where the (0, 0) means the stick is has not moved.
In the horizontal axes (first and third elements in axes
), -1
would indicate the stick is moved fully to the left, and 1 would mean the stick is moved fully to the right. In the vertical axes (second and fourth elements in axes
), -1
would indicate the stick is moved fully to the top, and 1 would mean the stick is moved fully to the bottom.
Here’s an example value of axes
with explanations in the comments:
setInterval(() => {
const myGamepad = navigator.getGamepads()[0]; // use the first gamepad
console.log(`Left stick at (${myGamepad.axes[0]}, ${myGamepad.axes[1]})` );
console.log(`Right stick at (${myGamepad.axes[2]}, ${myGamepad.axes[3]})` );
}, 100) // print axes 10 times per second
Contrary to buttons in HTML, event listeners cannot be added to gamepad buttons. Instead, you can check if a button is currently pressed by using the boolean pressed
property in the element in the buttons
array.
Here’s a list of button indexes are their Xbox and PS4 Equivalents in the HTML5 Gamepad API:
Index | Button .pressed Code |
Button on Xbox | Button on PlayStation |
---|---|---|---|
0 | gamepad.buttons[0].pressed |
A | X |
1 | gamepad.buttons[1].pressed |
B | O |
2 | gamepad.buttons[2].pressed |
X | Square |
3 | gamepad.buttons[3].pressed |
Y | Triangle |
4 | gamepad.buttons[4].pressed |
LB | L1 |
5 | gamepad.buttons[5].pressed |
RB | R1 |
6 | gamepad.buttons[6].pressed |
LT | L2 |
7 | gamepad.buttons[7].pressed |
RT | R2 |
8 | gamepad.buttons[8].pressed |
Show Address Bar | Share |
9 | gamepad.buttons[9].pressed |
Show Menu | Options |
10 | gamepad.buttons[10].pressed |
Left Stick Pressed | Left Stick Pressed |
11 | gamepad.buttons[11].pressed |
Right Stick Pressed | Right Stick Pressed |
12 | gamepad.buttons[12].pressed |
Directional Up | Directional Up |
13 | gamepad.buttons[13].pressed |
Directional Down | Directional Down |
14 | gamepad.buttons[14].pressed |
Directional Left | Directional Left |
15 | gamepad.buttons[15].pressed |
Directional Right | Directional Right |
16 | gamepad.buttons[16].pressed |
Xbox Light-Up Logo | PlayStation Logo |
Here's an example of checking if Button One (A on Xbox, X on PS4) is pressed:
const myGamepad = navigator.getGamepads()[0]; // use the first gamepad
setInterval(() => {
console.log(`Is Button One Pressed? ${myGamepad.buttons[0].pressed}`);
}, 1000 / 10) // check 10 times per second if the button one is pressed
Detect When a Gamepad Has Been Connected
The name of the event when a gamepad has been connected to the user’s machine is gamepadconnected
. The event argument that is passed into the event function includes a gamepad
property, which is a Gamepad
object for the gamepad the has been connected.
Instead of accessing this gamepad directly, it is more common to get the index of this gamepad in the navigator.getGamepads()
array by using the Gamepad.index
. For example:
// global gamepad object
let gamepadIndex;
window.addEventListener('gamepadconnected', (event) => {
gamepadIndex = event.gamepad.index;
});
// now print the axes on the connected gamepad, for example:
setInterval(() => {
if(gamepadIndex !== undefined) {
// a gamepad is connected and has an index
const myGamepad = navigator.getGamepads()[gamepadIndex];
console.log(`Left stick at (${myGamepad.axes[0]}, ${myGamepad.axes[1]})` );
console.log(`Right stick at (${myGamepad.axes[2]}, ${myGamepad.axes[3]})` );
}
}, 100) // print axes 10 times per second
A More Complicated Example
Here's an example program that displays which buttons on a controller are pressed at a given time. Try running this code and pressing buttons on your gamepad; you should see that the indexes of the buttons that are pressed is displayed.
<body>
<h1>No Controller Connected</h1>
</body>
<script type="text/javascript">
// global gamepad object
let gamepadIndex;
window.addEventListener('gamepadconnected', (event) => {
gamepadIndex = event.gamepad.index;
});
setInterval(() => {
if(gamepadIndex !== undefined) {
// a gamepad is connected and has an index
const myGamepad = navigator.getGamepads()[gamepadIndex];
document.body.innerHTML = ""; // reset page
myGamepad.buttons.map(e => e.pressed).forEach((isPressed, buttonIndex) => {
if(isPressed) {
// button is pressed; indicate this on the page
document.body.innerHTML += `<h1>Button ${buttonIndex} is pressed</h1>`;
}
})
}
}, 100) // print buttons that are pressed 10 times per second
</script>
Browser Support
The HTML5 Gamepad API has complete support in most modern web browsers today. However, there are a few browsers that don't yet support it as of December 2020, including:
- IE (11)
- Opera Mini
- Opera Mobile
- Android Browser
- KaiOS Browser
For more up-to-date information on browser support, see the Gamepad API's CanIUse Page.
To check if the browser supports the Gamepad API in JavaScript, the following code can be used:
const hasGamepadAPI = () => "getGamepads" in navigator;
Conclusion
I hope this helps in learning how to use the HTML5 Gamepad API. While the API is not yet widely used in online games at the moment, it can still be useful for a number of projects and can be fun to try out.
Thanks for scrolling.
— Gabriel Romualdo, December 15, 2020
Top comments (1)
This is a very well written introduction to the gamepad API. I wasn't aware that this was so widely supported by browsers and I'm really glad to read about it.
Thanks for writing this up.