Menu Search
Jump to the content X X

Today, too many websites are still inaccessible. In our new book Inclusive Design Patterns, we explore how to craft flexible front-end design patterns and make future-proof and accessible interfaces without extra effort. Hardcover, 312 pages. Get the book now →

Using The Gamepad API In Web Games

The Gamepad API is a relatively new piece of technology that allows us to access the state of connected gamepads using JavaScript, which is great news for HTML5 game developers.

A lot of game genres, such as racing and platform fighting games, rely on a gamepad rather than a keyboard and mouse for the best experience. This means these games can now be played on the web with the same gamepads that are used for consoles.

A demo is available1, and if you don’t have a gamepad, you can still enjoy the demo using a keyboard.

Keyboard Fallback Link

There are also apps like Joypad2 (iOS) and Ultimate Gamepad3 (Android) that allow you to connect a smartphone to the computer via a “receiver” app. Receiver applications simulate keyboard input, as opposed to a gamepad.

Which Gamepads Work? Link

Any gamepad that is understood by the operating system as either an XInput or DirectInput device will work with the Gamepad API. Getting other console controllers to work is possible, but that would require either hardware converters or additional software.

Gamepads4
Gamepads. (View large version5)

Browser Support Link

Because this API is relatively new and experimental, browser support is limited6 and the W3C documentation7 is still a working draft.

Nevertheless, browser implementation is above 50%8, which includes all major browsers. The API is even making its way into the mobile world, with Chrome for Android being the first mobile browser to support it.

On Nintendo’s Wii U browser, the Wii U gamepad is accessible. Unfortunately, Nintendo hasn’t yet implemented the Gamepad API; instead, the device has its own property9 on the window (window.wiiu.gamepad) and stores its button states as hexadecimal values, with specific flags for each button.

Feature Detection Link

We can check whether a browser supports the Gamepad API with this snippet:

if(!!navigator.getGamepads){
    // Browser supports the Gamepad API
}

Reading The Gamepad States Link

The Gamepad states are accessible with:

var gamepads = navigator.getGamepads();

Currently, in Chrome and Opera, this will return a GamepadList of up to four Gamepad objects.

GamepadList {0: Gamepad, 1: Gamepad, 2: undefined, 3: undefined}

Firefox, instead, returns a JavaScript Array of Gamepads, which, theoretically, is infinite. (I’ve had nine gamepads connected at the same time.)

Array [ Gamepad, Gamepad ]

Polling Link

We can poll the Gamepad states using requestAnimationFrame. Depending on the number of gamepads you want to support, this will increase the complexity of how you use the Gamepad states. If you want to support only one gamepad, you could poll for the first gamepad in the collection like so:

var gamepad = navigator.getGamepads()[0];

This means it will grab the first gamepad that is connected or, if all gamepads are already connected, the first gamepad to have a button pressed.

Each Gamepad object looks like this: Link

axes: Array[4]
buttons: Array[16]
connected: true
id: "Xbox 360 Controller (XInput STANDARD GAMEPAD)"
index: 0
mapping: "standard"
timestamp: 12

This is the Gamepad object of a Microsoft Xbox 360 controller for Windows10.

Note: The id is not consistent across browsers. For example, it’s 45e-28e-Wireless 360 Controller in Mozilla Firefox11, which is different from what Google Chrome12 provides above.

Info Link

Property Description
connected This is a boolean that indicates the gamepad’s connectivity
id This string contains identifying information about the gamepad
index This is a unique auto-incremented integer for each gamepad
mapping This string tells us whether the browser has remapped the device to a known layout
timestamp This increments when the device’s state changes. Some devices
constantly poll, which means the timestamp is constantly incrementing.
axes This is a collection of numbers that represent the state of each analogue stick or button.
buttons This collection of objects represents the state of each button.

Axes Link

On gamepads that have analogue joysticks, the axes array will contain numbers that range between a minimum and maximum for each axis, usually -1 and 1.

Applying a Dead Zone Link

Because the analogue stick varies between -1 and 1, if the stick is not being touched and is in its center position, then, theoretically, the value should be 0. However, this isn’t always the case on cheap controllers or gamepads that are worn or damaged or have “wiggly” thumb sticks (yes, that gamepad your friend always conveniently tries to give you).

A “dead zone” is a threshold used to prevent values below a certain amount from being used to control the game.

The following function applies a dead zone. The threshold is also subtracted from any value above the threshold, so that the value at the threshold is 0, rather than a sudden 0.25, for example.

var applyDeadzone = function(number, threshold){
   percentage = (Math.abs(number) - threshold) / (1 - threshold);

   if(percentage < 0)
      percentage = 0;

   return percentage * (number > 0 ? 1 : -1);
}

It can be used like this:

var joystickX = applyDeadzone(gamepad.axes[0], 0.25);

This ignores values between -0.25 and +0.25 and calculates the decimal percentage, taking into consideration the given threshold. If you applied this to both the x and y axes, this would give a dead zone that looks something like this when graphed:

deadzone13
Dead zone. (View large version14)

Note: Axes aren’t always analogue. Some controllers will toggle between values. For example, some D-pads toggle between a maximum, minimum and center value for each axis, such as -1, 0 and 1.

Buttons Link

For the buttons array, a GamepadButton object is provided for each item. In most cases, the value property directly correlates with the pressed property. For example, value will toggle between 0 and 1 because pressed toggles between false and true.

Released Button Link

GamepadButton
  pressed: false
  value: 0

Pressed Button Link

GamepadButton
  pressed: true
  value: 1

Analogue Buttons Link

Most seventh-generation gamepads (like the Xbox 360 controller) have analogue buttons, such as the left and right triggers. In this case, value will range from 0 to 1, respectively. Although pressed works, using it isn’t recommended for non-digital buttons; instead, use a threshold against the value, which could be as simple as this:

if(gamepad.buttons[7].value > 0.5){
  // FIRE!
}

Note: Because the button is also analogue, a dead zone might need to be applied here, too.

Varying Layouts Link

Each controller is different, which means the length of the axes and buttons will vary; this also applies to different connection converters and/or drivers. According to the specification, browsers should map the axes and buttons as closely as possible to the suggested “standard gamepad15.” Not all gamepads will have all of these buttons; missing buttons will appear as being in an unpressed state (for buttons) or neutral state (for axes) in the object. Adding a “control settings” area to your game would be a good idea if you plan to support weird and wonderful gamepads, giving players the freedom to configure their gamepad how they want and giving users with different gamepads the opportunity to manually map their controls to your game.

Microsoft Xbox 360 Controller for Windows Link

The driver I use to connect to a Mac is available on GitHub16.

360 Layout17
Microsoft Xbox 360 layout. (View large version18)

NES Controller Link

Here is the layout of an original NES controller, using a 15-pin-to-USB converter (the layout may differ by manufacturer). This particular one uses axes for directional input, rather than buttons.

Nes Layout19
NES layout. (View large version20)

Saitek SP550 USB Stick and Pad Link

This has 12 buttons (14 on the device, with the two pad triggers having the same functionality as the joystick’s trigger and thumb button) and three axes (the third axis being the throttle slider next to the joystick). The joystick part of the controller can be detached, leaving just the pad. Doing this will disconnect the controller and reconnect it as a different controller, meaning that details about the returned Gamepad object will change, too: The id will change from SP550 Stick & Pad Combo (Vendor: 06a3 Product: 100a) to SP550 Pad (Vendor: 06a3 Product: 100b), and the layout will be different also. In “stick and pad” mode, the directional pad is in the buttons array (four buttons), but in “pad” mode, the directional pad is in the axes array (two axes).

Saitek SP55021
Saitek SP550. (View large version22)

N64 Controller Link

This is one of my favorite controllers, mainly because of nostalgia, not ergonomics.

N64 Controller23
N64 controller. (View large version24)

Nintendo Wiimote and Nunchuck Link

With these drivers, it was possible to use the Wiimote and Nunchuk with the Gamepad API. Different drivers will result in different layouts.

For Windows, see Julian Löhr’s “HID Wiimote: A Windows Device Driver for the Nintendo Wii Remote25.”

For Mac, see Wjoy26, a Nintendo Wiimote driver for Mac OS X.

Wiimote Layout27
Wiimote layout. (View large version28)
Wiimote Nunchuck Layout29
Wiimote Nunchuck layout. (View large version30)

Charlie J. Walter has a demo for Wiimote31, which works only with the Windows driver.

For this demo, I created a calibration feature, because the drivers think that the Wiimote gyroscope’s neutral position (dead center) is the value when the controller is connected. This means you have to connect the controller when the controller is on a flat surface. However, you can recalibrate the controller easily enough by reading the value of the gyroscope (a value in axes) when the controller is on a flat surface, and then storing that value and subtracting it from any further readings.

Events Link

The Gamepad API comes with two window events, gamepadconnected and gamepaddisconnected, which trigger on connection and disconnection, respectively. The user must press a button on the connected gamepad in order for a gamepadconnected event to be recognized. The relevant Gamepad object is available on both event objects with e.gamepad.

Connection Events Link

window.addEventListener("gamepadconnected", function(e) {

  // Gamepad connected
  console.log("Gamepad connected", e.gamepad);
});
window.addEventListener("gamepaddisconnected", function(e) {

  // Gamepad disconnected
  console.log("Gamepad disconnected", e.gamepad);
});

State Change Events Link

State change events haven’t made it into the specification yet and require more discussion. However, Firefox already has three state change events: gamepadbuttondown, gamepadbuttonup and gamepadaxismove. These event names take on a similar naming convention to keyboard and mouse events. (Other suggestions include gamepadchanged and gamepadaxischanged.)

window.addEventListener("gamepadbuttondown", function(e){
   // Button down
   console.log(

      "Button down",
      e.button, // Index of button in buttons array
      e.gamepad

   );
});

window.addEventListener("gamepadbuttonup", function(e){
   // Button up
   console.log(

      "Button up",
      e.button, // Index of button in buttons array
      e.gamepad

   );
});

Note: Currently, these events work only in Firefox.

window.addEventListener("gamepadaxismove", function(e){

   // Axis move
   console.log(

      "Axes move",
      e.axis, // Index of axis in axes array
      e.value,
      e.gamepad

   );
});

Note: Currently this event works only in Firefox Nightly.

Using The API In A Game Link

However you render your game, you can get the state of the gamepad in every game loop and apply it to a controllable entity. Below is a very simple demo where the value of axes[0] is applied to the CSS left property.

See the Pen Gamepad API – DOM Element Demo32 by Charlie Walter (@cjonasw33) on CodePen34.

Of course, a complex game would be very demanding, so this approach probably isn’t the best idea. Instead, you could use WebGL or Canvas or use a rendering library such as Three.js35 or Babylon.js36. If the element doesn’t move, then your controller is probably mapped differently. This means axes[0] does not represent the joystick or analogue button that you are wiggling or is simply not mapped at all; try changing axes[0] to another element in the array. HTML5 Gamepad Tester37 has a visual breakdown of your connected gamepads and their properties.

Control Settings and Mapping Problems Link

Mapping differences can be fixed with a “control settings” utility as shown in Charlie J. Walter’s demo38.

How the Demo’s Control Settings Work Link

In the demo, the user clicks on an input box relating to their gamepad and the control they want to change or remap. The gamepad’s state is stored and constantly compared to the current state of that gamepad until an axis or button has changed more than 0.5 of its stored state. At that time, we will know which input the user wants to use for that game action (for example, “left,” “right,” “jump”) and the way they want to use it to represent that game action. The reason I say this is because the “left” and “right” actions will most likely be on the same axis, but the user will use the joystick in a slightly different way to represent a different action (for example, moving the joystick left for left and moving it right for right). This can be figured out by using the stored state of the changed button (but rounded, so that it is -1, 0 or 1) to represent the game action’s minimum state, and using the new (also rounded) state to be its maximum. These states are halved for buttons thresholds, the result being -0.5, 0 or 0.5.

How To Provide A Keyboard Fallback Link

The window events, onkeydown and onkeyup, make it easy to provide a keyboard fallback. We could add the code to make the player do something in these events, but because we are reading the gamepad’s each loop and using the axes and buttons arrays from the gamepad to then make the player do something, putting it here, too, makes sense. It also means less duplicate code.

One way we can do this is by having a keys collection to store the current keys down, which gets updated by onkeydown and onkeyup, respectively.

We can then do simple checks in the game loop to action the player.

// If right key down
if(keys[39]){

   // Move player right
}

Wrapping Up Link

As with all experimental technologies, results with the Gamepad API are unstable. However, by using it (and providing feedback), you are sculpting the future of the technology.

This represents a huge opportunity for the game industry. Many games already use front-end technologies like NW.js39 to create native apps; coupling them with the Gamepad API will make for games with a close-to-native experience. However, this is just one element of a rapidly growing gaming platform. Web technologies are now capable of many of the features we see in native games, including audio manipulation (via the Web Audio API), mouse movement input without screen restrictions (via the Pointer Lock API), touch gestures (via the Touch Event APIs) and many more.

In the future, I hope the Gamepad API will be able to access things like the rumble, internal speaker, microphone and other inputs.

To contribute to the future of the API, get involved in discussions and announce bugs when you find them in a given browser. I’d love to hear what you create with it!

(ds, ml, al, jb)

Footnotes Link

  1. 1 http://charliejwalter.net/gamepad/demos/main
  2. 2 http://getjoypad.com/legacy/
  3. 3 https://play.google.com/store/apps/details?id=com.negusoft.ugamepad&hl=en
  4. 4 https://www.smashingmagazine.com/wp-content/uploads/2016/01/01-gamepads-opt.jpg
  5. 5 https://www.smashingmagazine.com/wp-content/uploads/2016/01/01-gamepads-opt.jpg
  6. 6 http://caniuse.com/#feat=gamepad
  7. 7 http://www.w3.org/TR/gamepad/
  8. 8 http://caniuse.com/#feat=gamepad
  9. 9 https://www.nintendo.com/wiiu/built-in-software/browser-specs/extended-functionality/
  10. 10 https://www.microsoft.com/hardware/en-gb/p/xbox-360-controller-for-windows
  11. 11 https://www.mozilla.org/en-GB/firefox/new/
  12. 12 http://www.google.com/chrome/
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2016/01/02-deadzone-opt.png
  14. 14 https://www.smashingmagazine.com/wp-content/uploads/2016/01/02-deadzone-opt.png
  15. 15 http://www.w3.org/TR/gamepad/#remapping
  16. 16 https://github.com/d235j/360Controller/releases
  17. 17 https://www.smashingmagazine.com/wp-content/uploads/2016/01/03-360layout-opt.png
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2016/01/03-360layout-opt.png
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2016/01/04-neslayout-opt.png
  20. 20 https://www.smashingmagazine.com/wp-content/uploads/2016/01/04-neslayout-opt.png
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2016/01/05-sp550layout-opt.png
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2016/01/05-sp550layout-opt.png
  23. 23 https://www.smashingmagazine.com/wp-content/uploads/2016/01/06-n64layout-opt.png
  24. 24 https://www.smashingmagazine.com/wp-content/uploads/2016/01/06-n64layout-opt.png
  25. 25 http://julianloehr.de/educational-work/hid-wiimote/
  26. 26 https://github.com/alxn1/wjoy
  27. 27 https://www.smashingmagazine.com/wp-content/uploads/2016/01/07-wiimotelayout-opt.png
  28. 28 https://www.smashingmagazine.com/wp-content/uploads/2016/01/07-wiimotelayout-opt.png
  29. 29 https://www.smashingmagazine.com/wp-content/uploads/2016/01/08-wiimotenunchucklayout-opt.png
  30. 30 https://www.smashingmagazine.com/wp-content/uploads/2016/01/08-wiimotenunchucklayout-opt.png
  31. 31 http://charliejwalter.net/gamepad/demos/wiimote/car/
  32. 32 'http://codepen.io/cjonasw/pen/MwRQQW/'
  33. 33 'http://codepen.io/cjonasw'
  34. 34 'http://codepen.io'
  35. 35 http://threejs.org/
  36. 36 http://www.babylonjs.com/
  37. 37 http://html5gamepad.com/
  38. 38 http://charliejwalter.net/gamepad/demos/main
  39. 39 http://nwjs.io/

↑ Back to top Tweet itShare on Facebook

Advertisement

I am a Front End Developer with a passion for new technologies. I enjoy sharing what I know and learning what I don't. Follow me @cjonasw.

  1. 1

    Nicely explained for better understanding.

    2
  2. 2

    Great article – hope to see more games and apps using the Gamepad API.

    2
  3. 3

    Also very useful for creating switch accessible programs for people with physical disabilities!

    3
  4. 4

    Great information. I will use this knowledge for my buygosh.com Thank you very much. I hope to see more great post for all users this nice site!

    1

↑ Back to top