GamepadAPIを利用することで、ブラウザ上からゲームパッドにアクセスできるようになります。GamepadAPIはChrome、Firefox、Edgeが対応しています。
ゲームパッドAPIの利用方法
接続・切断を検出する
入力を検知するには、まずゲームパッドの接続を検出する必要があります。ゲームパッドの接続はgamepadconnectedイベント、切断はgamepaddisconnectedイベントで検出することができます。
ブラウザによっては、ゲームパッドを接続してページを開いただけではgamepadconnectedイベントが発火されないことがあります。これはセキュリティのためです。その場合は何かしらのボタンをおすことでgamepadconnectedイベントが発火されます。
addEventListener("gamepadconnected", (e) => {
    let gamepad = e.gamepad;
    console.log("ゲームパッドが接続されました。");
    console.log("Index: %d, ID: %s, Type: %s",
                gamepad.index, gamepad.id, gamepad.mapping);
});
addEventListener("gamepaddisconnected", (e) => {
    let gamepad = e.gamepad;
    console.log("ゲームパッドが切断されました。");
    console.log("Index: %d, ID: %s, Type: %s",
                gamepad.index, gamepad.id, gamepad.mapping);
});
現時点でのゲームパッドの入力情報を取得する
navigator.getGamepadsメソッドを使用することで、現時点で接続されている全てのゲームパッドの情報を取得することができます。navigator.getGamepadsメソッドはGamepadオブジェクトの配列を返すので、index値を利用して任意のゲームパッドの情報を取り出します。
addEventListener("gamepadconnected", (e) => {
    let connectedGamepadIndex = e.gamepad.index;
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];
});
ボタンが押されているかどうかを確認する
Gamepadオブジェクトはbuttons配列を持ちます。buttons配列にはGamepadButtonオブジェクトが格納されています。GamepadButtonオブジェクトは押下されているかどうかを表すpressedと、押されている強さを表すvalueプロパティを持ちます。通常、valueプロパティは0.0か1.0の値しか取りませんが、感圧式ボタンやLRトリガーなどの場合は0.0〜1.0の間の値を取ります。
let connectedGamepadIndex;
let loopID;
addEventListener("gamepadconnected", (e) => {
    connectedGamepadIndex = e.gamepad.index;
    loopID = requestAnimationFrame(loop);
});
addEventListener("gamepaddisconnected", (e) => {
    connectedGamepadIndex = null;
    cancelAnimationFrame(loopID);
});
// standardタイプのコントローラのマッピングです。
const BUTTON_A_INDEX     = 0;
const BUTTON_B_INDEX     = 1;
const BUTTON_X_INDEX     = 2;
const BUTTON_Y_INDEX     = 3;
const BUTTON_LB_INDEX    = 4;
const BUTTON_RB_INDEX    = 5;
const BUTTON_LT_INDEX    = 6;
const BUTTON_RT_INDEX    = 7;
const BUTTON_BACK_INDEX  = 8;
const BUTTON_START_INDEX = 9;
const BUTTON_L3_INDEX    = 10;
const BUTTON_R3_INDEX    = 11;
const BUTTON_UP_INDEX    = 12;
const BUTTON_DOWN_INDEX  = 13;
const BUTTON_LEFT_INDEX  = 14;
const BUTTON_RIGHT_INDEX = 15;
const BUTTON_HOME_INDEX  = 16;
function loop(timestamp) {
    // ゲームパッドの入力情報を毎フレーム取得します。
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];
    // ボタンが押されているかどうかを取得します。
    let aButton = gp.buttons[BUTTON_A_INDEX];
    if(aButton.pressed) {
        console.log(`Aボタンが押されました。value:${aButton.value}`);
    }
    requestAnimationFrame(loop);
}
スティックが倒されているかどうかを確認する
スティックについても同様です。Gamepadオブジェクトはaxesプロパティを持ちます。axesプロパティは配列で、0.0から1.0のdouble値が入っています。
let connectedGamepadIndex;
let loopID;
addEventListener("gamepadconnected", (e) => {
    connectedGamepadIndex = e.gamepad.index;
    loopID = requestAnimationFrame(loop);
});
addEventListener("gamepaddisconnected", (e) => {
    connectedGamepadIndex = null;
    cancelAnimationFrame(loopID);
});
// standardタイプのコントローラのマッピングです。
const AXIS_L_HORIZONTAL_INDEX = 0;
const AXIS_L_VERTICAL_INDEX   = 1;
const AXIS_R_HORIZONTAL_INDEX = 2;
const AXIS_R_VERTICAL_INDEX   = 3;
function loop(timestamp) {
    // ゲームパッドの入力情報を毎フレーム取得します。
    let gamepads = navigator.getGamepads();
    let gp = gamepads[connectedGamepadIndex];
    // スティックが倒されているかどうかを取得します。
    let leftAxisHorizontal = gp.axes[AXIS_L_HORIZONTAL_INDEX];
    let leftAxisVertical = gp.axes[AXIS_L_HORIZONTAL_INDEX];
    if(leftAxisHorizontal > 0 || leftAxisVertical > 0) {
        console.log(`Lスティックが倒されました。Hor:${leftAxisHorizontal}, Ver:${leftAxisVertical}`);
    }
    requestAnimationFrame(loop);
}
