Signatur
Beschreibung
navigator.getGamepads() ist der zentrale Einstiegspunkt der Gamepad API. Die Methode liefert ein Array, dessen Indizes den Gamepad.index-Werten der verbundenen Controller entsprechen. Freie Plätze, etwa nach dem Trennen eines Controllers, enthalten null. Das Array ist also dünn besetzt und kann Lücken haben.
Anders als bei Tastatur- oder Mausereignissen gibt es für Tastendrücke und Stickbewegungen keine Events. Den Zustand liest man stattdessen in einer Schleife ab, meist mit requestAnimationFrame(). Die Events gamepadconnected und gamepaddisconnected melden nur, dass ein Controller angeschlossen oder getrennt wurde.
In Chromium-basierten Browsern sind die zurückgegebenen Gamepad-Objekte Momentaufnahmen. Sie werden nicht automatisch aktualisiert. Deshalb sollte man getGamepads() in jedem Frame neu aufrufen, statt eine alte Referenz aus dem gamepadconnected-Event zu behalten. So verhält sich der Code in allen Browsern gleich.
Jedes Gamepad enthält unter anderem id, index, connected, mapping, timestamp, das Array buttons (Objekte vom Typ GamepadButton mit pressed, touched und value) sowie axes (Zahlen von -1 bis 1).
Rückgabewert
Gamepad-Objekt pro angeschlossenem Controller. Leere Plätze sind null. Solange der Nutzer noch keinen Knopf am Controller gedrückt hat, ist das Array leer oder enthält nur null.Beispiele
Verbundene Gamepads auflisten
// Erst nach einem Tastendruck am Controller sichtbar
window.addEventListener('gamepadconnected', () => {
const pads = navigator.getGamepads().filter(Boolean);
for (const pad of pads) {
console.log(`#${pad.index}: ${pad.id} (${pad.buttons.length} Tasten, ${pad.axes.length} Achsen, Mapping: ${pad.mapping || 'keins'})`);
}
});
Spielschleife mit Polling per requestAnimationFrame
const DEADZONE = 0.15;
const player = { x: 0, y: 0 };
const applyDeadzone = (v) => (Math.abs(v) < DEADZONE ? 0 : v);
const loop = () => {
// In jedem Frame neu abfragen – Objekte sind Momentaufnahmen
const pad = navigator.getGamepads()[0];
if (pad) {
player.x += applyDeadzone(pad.axes[0]) * 5;
player.y += applyDeadzone(pad.axes[1]) * 5;
// Taste 0 = A (Xbox) bzw. Kreuz (PlayStation) bei mapping 'standard'
if (pad.buttons[0].pressed) {
console.log('Sprung!', player);
}
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
Tastendruck-Flanken erkennen (gedrückt statt gehalten)
let previous = [];
const poll = () => {
const pad = navigator.getGamepads().find((p) => p?.connected);
if (pad) {
pad.buttons.forEach((btn, i) => {
if (btn.pressed && !previous[i]) {
console.log(`Taste ${i} gedrückt (Wert ${btn.value.toFixed(2)})`);
}
});
previous = pad.buttons.map((b) => b.pressed);
}
requestAnimationFrame(poll);
};
window.addEventListener('gamepadconnected', () => requestAnimationFrame(poll), { once: true });
// Wichtig · Fallstricke
- Datenschutz beim Fingerprinting: Browser zeigen Gamepads erst an, wenn der Nutzer auf der Seite einen Knopf gedrückt oder einen Stick bewegt hat. Vorher liefert
getGamepads()nur leere Einträge odernull. Auf diese erste Eingabe sollte man mitgamepadconnectedwarten. - Sicherer Kontext: Die Gamepad API ist nur über HTTPS (oder
localhost) verfügbar. In Iframes kann die Permissions-Policygamepadden Zugriff sperren. Dann wirft der Aufruf einenSecurityError. - Einträge mit
null: Vor dem Zugriff immer aufnullprüfen, z. B. mit?.oder.filter(Boolean). Außerdem lieferte ältere Chromium-Versionen eineGamepadListstatt eines echten Arrays. Wer sicher gehen will, wandelt das Ergebnis mitArray.from()um. - Tastenbelegung: Nur bei
mapping === 'standard'ist die Reihenfolge der Tasten und Achsen festgelegt. Bei anderen Controllern unterscheidet sich die Belegung je nach Gerät und Browser. - Totzone: Analogsticks liefern selbst in Ruhestellung selten exakt 0. Eine Totzone von etwa 0,1–0,2 verhindert, dass die Spielfigur von selbst wandert.
- Kompatibilität: Wird in allen aktuellen Browsern unterstützt. Das alte
navigator.webkitGetGamepads()ist veraltet. Hintergrund-Tabs erhalten oft keine Updates.