Something that is very easy to overlook among the many HTML5-based games out there now, many of which have sprung up in the last year or two, is that some browsers will detect and expose an API to use gamepads to play those games. It is not as well-known outside some games on the Google Play store and to those who watch the functionality added in different browser updates, but it is out there. You can use gamepads like the Xbox and PlayStation controllers to play some HTML5 games, if supported.
Unfortunately, the specification is still in flux and even support across browsers isn’t very strong. To my knowledge, only Chrome and Firefox use the Gamepad API and, even then, they both, as is often the case, disagree with how to implement it. In fact, not even Can I Use, the often go-to place for cross-browser support, has a listing for it. That is how rare it is.
However, just because support is scare doesn’t mean you can’t write code to use it now and, hopefully, in the future too. Gamepad support can be a bonus for those who have controllers and want to use them. It can be an extra selling point for your HTML5 project.
To detect support, we will first need to check it see if the navigator object has a property of ‘Gamepads’ through various vendor ids like ‘webkit’ or ‘moz’, and then populate a boolean value. However, in the case of Chrome, we will also need to check to see that its ‘webkitGetGamepads’ property is also a function. Instead of a property that is an array, Chrome’s implementation is a function that returns an array of gamepads.
If gamepad support is found, we need to separate out the two different ways it is currently implemented. One, the Firefox way, uses two events, connect and disconnect, to signal if a new gamepad has been added or removed. The second, Chrome way, is to expose all current gamepads at all times, relying on the developer or user to check statuses when needed.
To blend both implementations, we can write functions that match the Firefox method and then set up polling on the gamepads detected by either browser using the window.requestAnimationFrame function.
If the browser supports the Firefox method of different events, we need two different functions. One will check to see when or if a gamepad has been added. The other will remove the corresponding entry from the internal gamepad listing. Each will start polling once done.
Polling the gamepads is straightforward, with the function erasing and resetting the internal listing to whatever gamepads happen to be detected during that tick. For whatever is reported by the browser, those will be the new gamepads available for checking if any of their buttons have been pressed.
We expose the ‘pressed’ function to be called from the outside. It takes as its arguments the number of the gamepad, starting at zero, and what button should be checked using the internal mapping. The most common button, ‘FACE_1’, would be something like the ‘A’ button on a Xbox controller or the ‘X’ button on a PlayStation one.
If any of the axis buttons are asked for, we test the current position value against the set dead zone threshold and return if it has exceed it.
(In my code, I check against the XInput dead zone values Microsoft publishes.)
To get even more granular results from the axises, we use the ‘moved’ function and the arguments of which gamepad and axis to check. Assuming two joysticks on the controller, this allows for checking if the player has moved them only a smaller, set amount instead of if they are merely ‘pressed’ outside of their dead zone threshold.
At the bottom here, I’ve included the full code I’ve been using for projects as well as where I got my ideas and the dead zone values. I tend to lean more towards Chrome, so my solution combines both its and Firefox’s implementations. However, if you know you will only be using Firefox, you may want to consider its event-based model instead.
Note too that this code replies on the window.requestAnimationFrame function existing and without vendor prefixes. Usually, if you are combining this with another game library, that will be supplied for you. However, it isn’t, consider using Paul Irish’s rAF polyfill.