# How do you use a controller in JavaScript?
NicolasBrondinBernard
You're programming a web browser game with JavaScript and you want to add gamepad control? Nothing could be simpler!

Article published on 16/12/2024, last updated on 10/08/2026
Modern browsers include an API to manage game controllers (gamepads) so that you can use them in your web applications!
The API is very simple to use, but you need to fully understand how it works to avoid making mistakes.
The official documentation for this API is available from Mozilla!
Detecting the gamepad
The first step is to detect whether a gamepad is already connected to the machine, and store it in a variable.
navigator.getGamepads()lets you list all connected gamepads!
Here, if at least one gamepad is connected, we use it as the default controller:
let mainGamepad = null;
function tryLoadDefaultGamepad(){
const gamepads = navigator.getGamepads();
if(gamepads.length > 0){
mainGamepad = gamepads[0];
} else {
mainGamepad = null;
console.log("[DEBUG] Gamepad : 0 connected");
}
}
function init(){
tryLoadDefaultGamepad();
}
init();
Careful: Firefox and Chrome only detect a gamepad once there is a first interaction between the gamepad and the page (pressing a button or moving a joystick, for example)!
The code above will therefore not detect any gamepad when the page loads.
For our detection to be complete, we need to listen for the gamepadconnected and gamepaddisconnected events and act accordingly:
window.addEventListener("gamepadconnected", (e) => {
const index = e.gamepad.index;
const gamepads = navigator.getGamepads();
// every newly plugged-in gamepad becomes the main one
mainGamepad = gamepads[index];
console.log(`[DEBUG] Gamepad : Id ${e.gamepad.index} connected`);
});
window.addEventListener("gamepaddisconnected", (e) => {
if(e.gamepad.index === mainGamepad.index){
console.log(`[DEBUG] Gamepad : Id ${e.gamepad.index} disconnected`);
tryLoadDefaultGamepad();
}
});
With the complete code above, your page is now able to detect a gamepad!
Now let's see how to use our gamepad!
Managing buttons and joysticks
Since there are many different game controllers, with more or fewer buttons, joysticks, etc… It is generally recommended to create an interface (or an object) that will represent the controls available in our game.
Here we chose to add only two actions for simplicity, which makes it compatible with controllers having two buttons or more.
let controls = {
top: false,
left: false,
bottom: false,
right: false,
action1: false,
action2: false
};
Once we have the representation of our controls, we need to link these controls to interactions with our gamepad. For this we will use the buttons and axes objects provided by the API for each gamepad:
const joystickSensitivity = 0.5;
function updateControlsValues(){
if (mainGamepad) {
const buttons = mainGamepad.buttons;
const axes = mainGamepad.axes;
// La sensibilité permet d'éviter les problèmes de "drift" des joysticks
controls.top = axes[1] < -joystickSensitivity || buttons[12].pressed;
controls.bottom = axes[1] > joystickSensitivity || buttons[13].pressed;
controls.left = axes[0] < -joystickSensitivity || buttons[14].pressed;
controls.right = axes[0] > joystickSensitivity || buttons[15].pressed;
controls.action1 = buttons[0].pressed;
controls.action2 = buttons[1].pressed;
}
}
You've probably guessed it, the
axesobject represents the axes (horizontal and vertical) of our joystick
And for the rest:
buttons[0]represents theAbuttonbuttons[1]represents theBbuttonbuttons[12] to [15]represent the directional pad
But now that it is possible to update the state of our game controls based on the gamepad, we need to perform this update regularly.
So we will set up an infinite loop, which will be triggered in our init() function and called at regular intervals thanks to the requestAnimationFrame function:
function gameLoop(){
updateControlsValues();
console.log("[DEBUG] Controls :", controls);
requestAnimationFrame(gameLoop);
}
function init(){
tryLoadDefaultGamepad();
gameLoop();
}
init();
Why use a loop?
One might wonder why use a loop to check the gamepad's state at regular intervals, rather than listening for an event when a button is pressed.
And the question is legitimate, especially in JavaScript where everything is based on events.
But here's the thing: the reason is simply that the API does not provide any event when a button is pressed, or when a joystick moves.
This may seem strange, but it comes from a technical constraint related to the gamepad's electronics. Joysticks are not digital sensors, but analog ones.
This means that the slightest change in the joystick's position modifies the value.
And there are dozens of micro-movements per second! If JavaScript had to send an event for every change in the joystick's value, the web page's performance would be greatly impacted.
The complete code
Here is the entire code from the article, which you can copy and paste as you wish!
The program is strictly the same, but the organization has been slightly improved.
let mainGamepad = null;
const joystickSensitivity = 0.5;
let controls = {
top: false,
left: false,
bottom: false,
right: false,
action1: false,
action2: false
};
window.addEventListener("gamepadconnected", (e) => {
const index = e.gamepad.index;
const gamepads = navigator.getGamepads();
// chaque nouvelle manette branchée deviendra la principale
mainGamepad = gamepads[index];
console.log(`[DEBUG] Gamepad : Id ${e.gamepad.index} connected`);
});
window.addEventListener("gamepaddisconnected", (e) => {
if(e.gamepad.index === mainGamepad.index){
console.log(`[DEBUG] Gamepad : Id ${e.gamepad.index} disconnected`);
tryLoadDefaultGamepad();
}
});
function tryLoadDefaultGamepad(){
const gamepads = navigator.getGamepads();
if(gamepads.length > 0){
mainGamepad = gamepads[0];
} else {
mainGamepad = null;
console.log("[DEBUG] Gamepad : 0 connected");
}
}
function updateControlsValues(){
if (mainGamepad) {
const buttons = mainGamepad.buttons;
const axes = mainGamepad.axes;
// Sensitivity threshold for joysticks
controls.top = axes[1] < -joystickSensitivity || buttons[12].pressed;
controls.bottom = axes[1] > joystickSensitivity || buttons[13].pressed;
controls.left = axes[0] < -joystickSensitivity || buttons[14].pressed;
controls.right = axes[0] > joystickSensitivity || buttons[15].pressed;
controls.action1 = buttons[0].pressed;
controls.action2 = buttons[1].pressed;
}
}
function gameLoop(){
updateControlsValues();
console.log("[DEBUG] Controls :", controls);
requestAnimationFrame(gameLoop);
}
function init(){
tryLoadDefaultGamepad();
gameLoop();
}
init();
No spam. Only free content, news, and ever more resources to level up your skills!
Join +1500 developers
No comments yet