Gamepad
Mandos de consola en la web, con vibración y gatillos hápticos.
getGamepads() da el estado de botones y ejes de cada mando; con el mapeo «standard» la disposición es la misma en mandos de Xbox, PlayStation o Switch. vibrationActuator.playEffect() hace vibrar el mando y, con mandos tipo Xbox en Chrome, también los gatillos.
La lectura es Baseline desde 2017 (ampliamente disponible desde 2019). La vibración está en Chrome, Edge y Safari para macOS; Firefox no la implementa. Por privacidad, ningún mando aparece hasta que el usuario pulsa un botón.
Para qué sirve
- Juegos web con mando
- Control de presentaciones, robots o instalaciones
- Accesibilidad con mandos adaptados
Demo
Conecta un mando (USB o Bluetooth) y pulsa cualquier botón: por privacidad, el navegador no lo muestra hasta entonces.
Compatibilidad
Baseline · ampliamente disponible · 27 sep 2019| Función | Escritorio | Móvil | |||||
|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS | |
| getGamepads() | 35Chrome: 35 | 12Edge: 12 | 29Firefox: 29 | 10.1Safari: 10.1 | 35Chrome: 35 | 32Firefox: 32 | 10.3Safari: 10.3 |
| Vibración (dual-rumble) | 68Chrome: 68 | 79Edge: 79 | —Firefox: No | 16.41Safari: 16.4 | 68Chrome: 68 | —Firefox: No | —Safari: No |
| Gatillos (trigger-rumble) | 1262Chrome: 126 | 126Edge: 126 | —Firefox: No | —Safari: No | 126Parcial3Chrome: 126 Parcial | —Firefox: No | —Safari: No |
- 1Solo en macOS.
- 2Windows y macOS; en Linux y ChromeOS, solo por Bluetooth. Mandos tipo Xbox.
- 3La API existe, pero los gatillos no vibran en Android.
Compatibilidad generada en cada publicación desde MDN browser-compat-data 8.1.3 (24 sep 2026) · web-features 3.40.0 · revisión manual: 1 oct 2026
Código mínimo
<!doctype html>
<meta charset="utf-8">
<title>Gamepad · ejemplo mínimo</title>
<!-- Conecta un mando y pulsa un botón: el navegador solo lo muestra tras la primera pulsación. -->
<p id="status">Pulsa un botón del mando…</p>
<pre id="state"></pre>
<button id="rumble" disabled>Vibrar</button>
<script>
const $ = (s) => document.querySelector(s);
let index = null;
addEventListener('gamepadconnected', (e) => {
index = e.gamepad.index;
$('#status').textContent = `Conectado: ${e.gamepad.id} (${e.gamepad.mapping || 'sin mapeo estándar'})`;
$('#rumble').disabled = !e.gamepad.vibrationActuator;
requestAnimationFrame(loop);
});
addEventListener('gamepaddisconnected', () => { index = null; $('#status').textContent = 'Desconectado'; });
// El estado no llega por eventos: se consulta en cada fotograma
function loop() {
const pad = index === null ? null : navigator.getGamepads()[index];
if (!pad) return;
const pressed = pad.buttons.flatMap((b, i) => (b.pressed ? [i] : []));
$('#state').textContent =
`Botones pulsados: ${pressed.join(', ') || '—'}\n` +
`Ejes: ${pad.axes.map((a) => a.toFixed(2)).join(' ')}`;
requestAnimationFrame(loop);
}
// Vibración: 'trigger-rumble' en los gatillos de mandos Xbox (Chrome), si no 'dual-rumble'
$('#rumble').onclick = () => {
const actuator = navigator.getGamepads()[index]?.vibrationActuator;
const effect = actuator?.effects?.includes('trigger-rumble') ? 'trigger-rumble' : 'dual-rumble';
actuator?.playEffect(effect, {
duration: 400, strongMagnitude: 1, weakMagnitude: 0.5, leftTrigger: 0.8, rightTrigger: 0.8,
});
};
</script><!doctype html>
<meta charset="utf-8">
<title>Gamepad · ejemplo mínimo</title>
<!-- Conecta un mando y pulsa un botón: el navegador solo lo muestra tras la primera pulsación. -->
<p id="status">Pulsa un botón del mando…</p>
<pre id="state"></pre>
<button id="rumble" disabled>Vibrar</button>
<script>
const $ = (s) => document.querySelector(s);
let index = null;
addEventListener('gamepadconnected', (e) => {
index = e.gamepad.index;
$('#status').textContent = `Conectado: ${e.gamepad.id} (${e.gamepad.mapping || 'sin mapeo estándar'})`;
$('#rumble').disabled = !e.gamepad.vibrationActuator;
requestAnimationFrame(loop);
});
addEventListener('gamepaddisconnected', () => { index = null; $('#status').textContent = 'Desconectado'; });
// El estado no llega por eventos: se consulta en cada fotograma
function loop() {
const pad = index === null ? null : navigator.getGamepads()[index];
if (!pad) return;
const pressed = pad.buttons.flatMap((b, i) => (b.pressed ? [i] : []));
$('#state').textContent =
`Botones pulsados: ${pressed.join(', ') || '—'}\n` +
`Ejes: ${pad.axes.map((a) => a.toFixed(2)).join(' ')}`;
requestAnimationFrame(loop);
}
// Vibración: 'trigger-rumble' en los gatillos de mandos Xbox (Chrome), si no 'dual-rumble'
$('#rumble').onclick = () => {
const actuator = navigator.getGamepads()[index]?.vibrationActuator;
const effect = actuator?.effects?.includes('trigger-rumble') ? 'trigger-rumble' : 'dual-rumble';
actuator?.playEffect(effect, {
duration: 400, strongMagnitude: 1, weakMagnitude: 0.5, leftTrigger: 0.8, rightTrigger: 0.8,
});
};
</script>Novedades
Ten en cuenta
- El estado no llega por eventos: consulta getGamepads() en cada fotograma (requestAnimationFrame).
- Firefox exige HTTPS. En iframes de otro origen, Chrome y Edge (103+) piden allow="gamepad".
- En Chrome, un efecto de más de 5 segundos (duración más retardo) se rechaza como invalid-parameter.
Más información
Fuentes
- W3C · Gamepad (Editor’s Draft)consultado el 1 oct 2026
- Chrome 149 release notes2 jun 2026
- Blink · Intent to Ship trigger-rumbleabr 2024
- MDN browser-compat-data 8.1.324 sep 2026