HTML5 Gamepad API on Chrome

11.5k views Asked by At

I'm using Chrome (Version 19.0.1084.46). I enabled the Gamepad API in chrome://flags. I plugged in some game pads, but navigator.webkitGamepads is always an array of length 4 containing only undefined.

navigator.webkitGamepads
GamepadList
  0: undefined
  1: undefined
  2: undefined
  3: undefined
  length: 4
  __proto__: GamepadList

What do I need to do to test out using gamepads? I'm on Ubuntu Linux if that matters.

3

There are 3 answers

0
Matt On BEST ANSWER

I was having trouble with this as well (on Ubuntu 10.04 with Chrome 21.0.1163.0 dev). I ran across this from a thread on chromium-discussions:

Note that you need to press a face button on the gamepad before data will be available. This is due to fingerprinting concerns.

I wrote a quick test page that seems to work if you hold a controller button down while refreshing the page. I'm using a Gamestop-branded Xbox 360 wired controller with the xboxdrv driver.

Also, one other important thing to note - Chrome treats these Gamepad objects like snapshots of the controller state. So if you pass around the actual Gamepad object, you'll never get updated information. Chrome seems to only poll the controller when you call the navigator.webkitGamepads[x] getter (see line 23 in my test page).

1
Silviu-Marian On

This didn't work for me but maybe it helps you? Pulled from here.

function updateStatus() {
    window.webkitRequestAnimationFrame(updateStatus);

    var gamepads = navigator.webkitGamepads;

    var data = '';
    for (var padindex = 0; padindex < gamepads.length; ++padindex)
    {
        var pad = gamepads[padindex];
        if (!pad) continue;
        data += '<pre>' + pad.index + ": " + pad.id + "<br/>";
        for (var i = 0; i < pad.buttons.length; ++i)
            data += "button" + i + ": " + pad.buttons[i] + "<br/>";
        for (var i = 0; i < pad.axes.length; ++i)
            data += "axis" + i + ": " + pad.axes[i] + "<br/>";
    }
    document.body.innerHTML = data;
}

window.webkitRequestAnimationFrame(updateStatus);

Or as a hard alternative, there's the Javascript Joystick Plug-in (demo here) but I don't think that works in Linux.

0
David Edwards On

SPECIAL NOTE: The GamePad API is handled in two completely different ways by Firefox and Google Chrome. Because of this, you need to include code to test for your browser's identity, and handle the cases accordingly.

When the navigator.getGamePads() method is invoked, an array of objects is returned, and those objects contain information about the identity of your gamepads/joysticks, and the state of the axes/buttons thereof.

Here's the issue: on Firefox, the method hands the array to your calling code, and you can then examine that array repeatedly, to look for changes of axis/button state in your own code. Firefox behaves in the expected manner, and updates the objects you've received with new axis/button data, as and when it becomes available.

Google Chrome, on the other hand, treats the array totally differently. Chrome treats the array as a one-time snapshot of the status of your gamepads/joysticks, and if you want to fetch new data, you have to perform another invocation of navigator.getGamePads(), to fetch a new snapshot, discarding the old one.

As a consequence, you need something like this to make your code work on both browsers (assuming that the variable 'gp' is the variable in which you previously stored the return value from your first invocation of navigator.getGamePads() ...)

var ua = navigator.userAgent;
if (ua.toLowerCase().indexOf("chrome") != -1)
    gp = navigator.getGamePads();

Why the two browsers behave so differently, is a topic for another thread, but I suspect it has much to do with the fact that the GamePad API is still somewhat experimental, and the fine detail of the standard applicable thereto has not yet been finalised.

In my case, running Chrome on Windows 7 64-bit (but the 32-bit version, for some strange reason best known to my computer's manufacturer, who installed it in this fashion), the array returned by navigator.getGamePads() currently contains only as many entries as there are actual gamepads/joysticks plugged into my computer. If you're experiencing something different on Chrome under Linux, then you need to take this into account also, by testing to see if you have any null values in the array.

Another factor to take into account, is that when selecting a gamepad/joystick, the standard in place calls for the index property of the GamePad object to be referenced, and used thereafter to index into the array. The reason for this is covered in more detail in this document:

W3C page : Editor's Draft on the GamePad interface

However, whilst the implementation of this works as documented above in my incarnation of Google Chrome, you may have to check your version experimentally to see if it behaves the same as mine. If it doesn't, adjust your code accordingly until such time as the standard is finalised, and proper conformity thereto is enforced in all modern browsers and all OS incarnations thereof.

So, when you're selecting a particular gamepad/joystick to work with, the code will look something like this (with the variable 'gid' as your index selector):

var gLen = this.gamePads.length;
done = false;
idx = 0;

while (!done)
{
    if (this.gamePads[idx] !== null)
    {
        if (gid == this.gamePads[idx].index)
        {
        //Here, choose this GamePad as the selected GamePad, based upon the index number as per the W3C recommendation ...

            selectedGamePadIndex = gid;
            selectedGamePad = this.gamePads[idx];
            done = true;

        //End if
        }

    //End if
    }

    //Update counter in case we haven't selected one of the available GamePads above ...

    idx++;

    if (idx >= gLen)
        done = true;    //Exit loop altogether if we've exhausted the list of available GamePads

//End while
}

I actually implemented the above code (along with some tidying up at the end) as a method for a custom object, but the principle remains the same regardless of the implementation minutiae. (I'll confess at this juncture that I have a habit of writing my own short libraries to handle tasks like this!)

Another issue to watch out for, is the manner in which the gamepad API maps the physical data sources of your gamepad/joystick to the axis/button elements of the GamePad object, and the values contained therein. I have a Microsoft Sidewinder Pro USB joystick, and the mappings for this device on my computer are seriously weird, viz:

  • Axes 0/1: Joystick handle (as expected)
  • Axes 2/3: Not assigned
  • Axes 4/5: 4 not assigned, 5 assigned to twist grip
  • Axes 6/7: 6 assigned to round slider, 7 not assigned
  • Axes 8/9: 8 not assigned, 9 assigned to hat switch (er, WHAT???)

Yes, that's right, analogue axis 9 is assigned to the hat switch, and seriously odd values are returned, viz:

  • Hat switch centred: +1.2
  • Hat switch up: -1.0
  • Hat switch down: +0.1
  • Hat switch left: +0.7
  • Hat switch right: -0.42

Quirks like this are something you should be alert to as you experiment with the GamePad API. Even worse, a so-called "standard" GamePad (defined in that document I linked to above) may report as standard on some browsers, but not on others, or worse still, report as standard on browser X in Windows, but not on the same browser X in Linux! While this will induce much hair-tearing frustration as you try to fight your way through this software equivalent of bramble thicket, getting badly scratched by some of the thorns along the way, you can at least take comfort in the fact that browser developers are working to try and ameliorate this, but it'll take time, because other things take priority (such as making sure your browser doesn't become an easy target for ransomware to hijack, which will ruin you day immensely if it happens).