Coding ยท ยท

Using USB and Bluetooth gamepads

https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API

Here's some sample code, which you can put in an index.html and run with bun index.html:

<!doctype html>
<title>Game controller</title>
<h1>Game controller</h1>
<p>Minimal code to connect controller and use d-pad to move, 'x' to 'fire'. See <a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API">MDN for more</a> or <a href="/example.html">see all the input sources</a> your gamepad has.</p>
<canvas id="canvas" style="border: 1px black solid;"></canvas>
<script>
let ctx = canvas.getContext('2d')

let hero = {
  x: canvas.width / 2,
  y: canvas.height / 2,
  speed: 3,
  active: false
}

gameLoop()

function gameLoop () {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  let gamepads = navigator.getGamepads()
  for (let gamepad of gamepads) {
    if (gamepad) {
      console.log(gamepad.buttons)
      if (gamepad.buttons[12].pressed) hero.y-= hero.speed
      if (gamepad.buttons[13].pressed) hero.y+= hero.speed
      if (gamepad.buttons[14].pressed) hero.x-= hero.speed
      if (gamepad.buttons[15].pressed) hero.x+= hero.speed
      hero.active = gamepad.buttons[0].pressed
    }
  }

  ctx.fillStyle = hero.active ? 'red' : 'black'
  ctx.fillRect(hero.x, hero.y, 10, 10)
  requestAnimationFrame(gameLoop)
}
</script>

... this code is also running online at https://gamepad.deno.dev/.

And some more sample code:

<!doctype html>
<h1>Connect gamepad to see buttons and other inputs</h1>
<p>Code straight from <a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API#complete_example_displaying_gamepad_state">MDN for more</a>.</p>
<script>
  let loopStarted = false;

window.addEventListener("gamepadconnected", (evt) => {
  addGamepad(evt.gamepad);
});
window.addEventListener("gamepaddisconnected", (evt) => {
  removeGamepad(evt.gamepad);
});

function addGamepad(gamepad) {
  const d = document.createElement("div");
  d.setAttribute("id", `controller${gamepad.index}`);

  const t = document.createElement("h1");
  t.textContent = `gamepad: ${gamepad.id}`;
  d.append(t);

  const b = document.createElement("ul");
  b.className = "buttons";
  gamepad.buttons.forEach((button, i) => {
    const e = document.createElement("li");
    e.className = "button";
    e.textContent = `Button ${i}`;
    b.append(e);
  });

  d.append(b);

  const a = document.createElement("div");
  a.className = "axes";

  gamepad.axes.forEach((axis, i) => {
    const p = document.createElement("progress");
    p.className = "axis";
    p.setAttribute("max", "2");
    p.setAttribute("value", "1");
    p.textContent = i;
    a.append(p);
  });

  d.appendChild(a);

  // See https://github.com/luser/gamepadtest/blob/master/index.html
  const start = document.querySelector("#start");
  if (start) {
    start.style.display = "none";
  }

  document.body.append(d);
  if (!loopStarted) {
    requestAnimationFrame(updateStatus);
    loopStarted = true;
  }
}

function removeGamepad(gamepad) {
  document.querySelector(`#controller${gamepad.index}`).remove();
}

function updateStatus() {
  for (const gamepad of navigator.getGamepads()) {
    if (!gamepad) continue;

    const d = document.getElementById(`controller${gamepad.index}`);
    const buttonElements = d.getElementsByClassName("button");

    for (const [i, button] of gamepad.buttons.entries()) {
      const el = buttonElements[i];

      const pct = `${Math.round(button.value * 100)}%`;
      el.style.backgroundSize = `${pct} ${pct}`;
      if (button.pressed) {
        el.textContent = `Button ${i} [PRESSED]`;
        el.style.color = "#42f593";
        el.className = "button pressed";
      } else {
        el.textContent = `Button ${i}`;
        el.style.color = "#2e2d33";
        el.className = "button";
      }
    }

    const axisElements = d.getElementsByClassName("axis");
    for (const [i, axis] of gamepad.axes.entries()) {
      const el = axisElements[i];
      el.textContent = `${i}: ${axis.toFixed(4)}`;
      el.setAttribute("value", axis + 1);
    }
  }

  requestAnimationFrame(updateStatus);
}

</script>