Signatur
Beschreibung
Gamepad.axes gibt ein schreibgeschütztes Array von Gleitkommazahlen zurück. Jeder Eintrag steht für eine Achse des Controllers, also meist die horizontale oder vertikale Richtung eines Analogsticks. Die Werte liegen zwischen -1.0 (ganz links bzw. ganz oben) und 1.0 (ganz rechts bzw. ganz unten). In Ruhestellung liegt der Wert bei ungefähr 0.
Bei Controllern mit gamepad.mapping === "standard" ist die Belegung festgelegt:
axes[0]: linker Stick, horizontalaxes[1]: linker Stick, vertikalaxes[2]: rechter Stick, horizontalaxes[3]: rechter Stick, vertikal
Nicht standardisierte Geräte können mehr oder weniger Achsen haben und eine beliebige Reihenfolge verwenden. Die analogen Schultertasten (Trigger) gehören im Standard-Layout nicht zu axes, sondern zu buttons[6] und buttons[7] (dort über .value).
Die Gamepad API arbeitet mit Abfragen statt mit Ereignissen. Um aktuelle Achsenwerte zu erhalten, ruft man in jedem Frame navigator.getGamepads() auf, typischerweise innerhalb einer requestAnimationFrame-Schleife, und liest danach axes aus.
Rückgabewert
number-Werten von -1.0 bis 1.0, ein Eintrag pro Achse. Die Reihenfolge richtet sich nach Gamepad.mapping.Beispiele
Achsenwerte bei Verbindung ausgeben
window.addEventListener('gamepadconnected', (event) => {
const gp = event.gamepad;
console.log(`${gp.id}: ${gp.axes.length} Achsen, Mapping: "${gp.mapping}"`);
console.log('Startwerte:', gp.axes.map((v) => v.toFixed(2)));
});
Spielfigur per linkem Stick in einer Game-Loop steuern
const DEADZONE = 0.15;
const SPEED = 5; // Pixel pro Frame
const player = { x: 200, y: 200 };
// Kleine Ausschläge (Stick-Drift) ignorieren
const applyDeadzone = (v) => (Math.abs(v) < DEADZONE ? 0 : v);
const loop = () => {
// Bei jedem Frame neu abfragen, gespeicherte Objekte können veraltet sein
const [gp] = navigator.getGamepads().filter(Boolean);
if (gp) {
const dx = applyDeadzone(gp.axes[0] ?? 0);
const dy = applyDeadzone(gp.axes[1] ?? 0);
player.x += dx * SPEED;
player.y += dy * SPEED;
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
Radiale Deadzone und Richtungswinkel des rechten Sticks
const readStick = (gp, xIndex, yIndex, deadzone = 0.2) => {
const x = gp.axes[xIndex] ?? 0;
const y = gp.axes[yIndex] ?? 0;
const magnitude = Math.min(Math.hypot(x, y), 1);
if (magnitude < deadzone) return { magnitude: 0, angle: null };
// Auf 0..1 umrechnen, damit die Bewegung nach der Deadzone weich einsetzt
const scaled = (magnitude - deadzone) / (1 - deadzone);
const angle = Math.atan2(y, x) * (180 / Math.PI);
return { magnitude: scaled, angle };
};
const gp = navigator.getGamepads().find((g) => g?.mapping === 'standard');
if (gp) {
console.log(readStick(gp, 2, 3));
}
// Wichtig · Fallstricke
- Stick-Drift: Analogsticks liefern in Ruhestellung selten genau
0. Deshalb immer eine Deadzone verwenden, meist zwischen 0,1 und 0,25. Eine radiale Deadzone über den Betrag beider Achsen fühlt sich natürlicher an als eine Prüfung jeder Achse für sich. - Y-Achse: Negative Werte bedeuten „oben“, positive „unten“. Für eine 3D-Kamera muss man die Achse oft umkehren.
- Veraltete Daten: In Chromium-Browsern ist das
Gamepad-Objekt ein Schnappschuss. Es wird nicht automatisch aktualisiert.navigator.getGamepads()deshalb in jedem Frame neu aufrufen, statt das Objekt ausgamepadconnectedzu speichern. - Belegung nicht garantiert: Nur bei
mapping === "standard"ist die Bedeutung der Indizes festgelegt. Bei anderen Geräten können Anzahl und Reihenfolge der Achsen abweichen, manche liefern das Steuerkreuz sogar als Achse. Mit?? 0absichern und eine Tastenbelegung durch den Nutzer anbieten. - Kompatibilität und Sicherheit: Die API wird von allen modernen Browsern unterstützt, aber nur in sicheren Kontexten (HTTPS). Außerdem gibt der Browser Gamepads zum Schutz vor Fingerprinting erst frei, nachdem der Nutzer eine Taste gedrückt oder einen Stick bewegt hat. Vorher liefert
getGamepads()nurnull-Einträge. PerPermissions-Policy: gamepadkann die API in iframes gesperrt sein.