Designing for Television, Part 3

Using the Gamepad API and Controller.js

Samir Zahran
Sep 8, 2016 · 8 min read

In Part 2, we walked you through building a prototype for TV interfaces. Now we’ll discuss the Controller.js library we used and the underlying Gamepad API that powers it.

Modern web browsers feature support for gaming controllers natively through the Gamepad API. Although it’s only in the W3C Draft stage, many web browsers are already implementing basic support.

While it’s powerful on its own, it was primarily designed for games and lacks the features and ease of use we thought would be helpful for building interactive UIs. In this article we’re going to discuss the capabilities of the Gamepad API and the limitations and missing features which led us to develop Controller.js.


Here’s how the Gamepad API works

The Gamepad API is a web technology that gives JavaScript developers access to gamepads that are connected to a user’s computer. The API itself has a small feature-set. It can fire two DOM Events: gamepadconnected and gamepaddisconnected. Unlike keyboard and mouse interactions, the buttons and analog sticks on a gamepad do not fire any events. Instead, to get information about what a user is doing, you have to set up a loop to continually ask the gamepad for it’s current state.

This approach makes sense in the context of game development. If you’re writing a game, you’ll probably have a render loop to draw the scene each frame using requestAnimationFrame(), so checking a gamepad’s state near the beginning of that loop is a convenient and efficient way to update your game based on user input.

The signals sent from gamepads are broken into two arrays: buttons and axes. Because there are a variety of gamepads in the market, and no actual standards for their layouts, the Gamepad API makes no requirements about the length of these arrays nor the order in which buttons and axes must appear in them. The W3C Draft does define a single layout that it calls the “Standard Gamepad”, based on de facto standards that many gamepads have settled upon, which recommends which indexes the triggers, shoulder buttons, analog sticks, D-pad and other buttons should be mapped to. If a gamepad is recognized by the browser, the indexes will match up with this layout (as seen in the diagram below). Otherwise, they will not, e.g. buttons[7] may not be the right trigger.

The W3C’s Standard Gamepad

What the API is missing

What drove us to develop Controller.js were a combination of things that we either A) wanted as general features or B) thought would make interacting with gamepads more “web-like”.

  1. A consistent naming scheme for inputs and the ability to map non-standard layout gamepads to those names
  2. An event-driven system for buttons and analog sticks more consistent with other web development paradigms
  3. A simple way to treat analog stick movements as if they were D-pad presses (and other configurable settings)

1. Layouts and input names

Controller.js tries to help the developer out in two ways. The first is assigning descriptive names to each input defined in the Standard Gamepad layout, and the second is maintaining a list of gamepads and instructions for how to map their unique layouts to those names that can be used as a fallback if that gamepad is not natively recognized by the browser.

A list of the button names available for Standard Gamepad layouts

When a gamepad is connected the library checks if it is recognized by the browser, and if so maps names to the correct inputs according to the Standard Gamepad layout. If not, it searches for a match in the Controller.layouts.js add-on and uses that layout to correctly assign names to the inputs.

One area in which Controller.js differs more fundamentally from the Gamepad API is in the treatment of analog sticks. The Gamepad API represents each as two separate axes, one for horizontal position and the other for vertical. Controller.js combines each pair of axes into a single input, an “analog stick” with an X and Y value. As a result, a typical gamepad will report two analog sticks (LEFT_ANALOG_STICK and RIGHT_ANALOG_STICK), instead of four axes.

Controller.js combines axes into LEFT_ANALOG_STICK and RIGHT_ANALOG_STICK

2. Events

The library provides a few events each for buttons and analog sticks. Buttons have events for press, hold and release while analog sticks have start, hold, change and end. The library does the heavy lifting of comparing an input’s value changes over time to determine what is happening and dispatch the appropriate event in each situation. This frees you, the developer, to focus on your app.

When an event fires, it passes data back to your application. Some information—like the index of the gamepad that fired the event, the time the event fired and the name of the input—are available for both buttons and analog sticks, but each has it’s own unique properties as well.

Buttons

  • pressed tells you whether the button is pressed or not
  • value is a number between 0 and 1 telling you how far the button has been pressed (pressure sensitive buttons such as triggers can have values between 0 and 1)

Analog Sticks

  • position.x and position.y are the horizontal and vertical axis values, each from -1 to 1
  • angle.degrees is the angle the stick is pressed in degrees, with 0° being right, 90° being up, and so on
  • angle.radians is also the angle the stick is pressed but represented in radians, with right at 0 and left at π (~3.14)
Both angle values are NaN (Not-A-Number) when x and y are both 0

3. Settings

This led to a more robust system of controller settings that could be set and tweaked at runtime. For example, we found it helpful to set a threshold for what value on a button constitues a “press”, or how far an analog stick needed to be moved in any direction for it to register as a D-pad press.

Gamepad compatibility

Support is a mixed bag. First, your operating system has to recognize the gamepad. Some will work out of the box, others may have official drivers which need to be installed, and still others you can only get to work with unofficial, third party drivers or not at all.

If the OS does recognize the gamepad, it’s then up to the browser you’re using to recognize it. There are three possibilities:

  1. It sees the gamepad and recognizes it (Standard Gamepad layout)
  2. It sees the gamepad but does not recognize it (unknown layout)
  3. It doesn’t see the gamepad at all

With the third possibility, your gamepad is essentially invisible to the Gamepad API and, by extension, Controller.js.

The second possibility is where things can get confusing, and it’s why the Controller.layouts.js extension exists. It’s a brute-force method of making sense of the gamepads your browser sees but does not understand by creating a list of gamepads and how their inputs are mapped.

To illustrate this, let’s look at the Xbox One Controller. As of the time of writing this, Windows officially supports this gamepad. Using Edge on Windows 10, it will be visible and conform to the Standard Gamepad layout. However, there is no official driver for macOS, so we turned to one from a third party. With this installed, both Chrome and Firefox recognize the gamepad but both reported different layouts (neither of them the Standard Gamepad layout). Controller.layouts.js contains support for both of these layouts and knows which to use based on the user’s browser.

Getting started

Let’s take a look at a few examples to get you using and understanding the basics of Controller.js quickly.

Discovering gamepads

Once you run this method, any gamepads you connect will become available and begin reporting events, starting with gc.controller.found, to let you know it’s been discovered.

Reacting to button events

In the following example we register three events — when a button is pressed, when it is released and whenever an analog stick moves — and write a simple message to the JavaScript console for each.

Configuring settings

A particularly useful setting is useAnalogAsDpad, which makes one or both analog sticks fire D-pad button events when they’re pressed beyond a threshold in any direction.


These are just the basics but they’re enough to get started. For a deep-dive into the API, check out the documentation or the Controller.js homepage.

Related Links


Controller.js was made by Samir Zahran at This Also and is shared under an MIT License.

This Also

We help companies design products for mobile & beyond.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store