javascriptevent-handlinggamepad

How to create a generic "joystick/gamepad event" in Javascript?


Issue:
In the current implementation of modern browsers, (like Firefox or Chrome), there are only two joystick/gamepad events:

Since it appears that the original idea behind implementing joystick/gamepad support in the browser was to allow for in-browser games, the joystick was made dependents on the requestAnimationFrame() call to create a game-loop sync'd with v_sync.

However, in other use cases, for example where the joystick is being used to control something remotely over a network or wireless connection, the best case is to only send data when there is something useful to say - did something happen?  Using requestAnimationFrame() floods the interface with potentially useless data.

Unfortunately, there is currently no established interface for triggering gamepad events.&nbsp: (Note, there is some discussion of this very issue over on the Mozilla and W3C forums, so this may, eventually, change.)

Since flooding an industrial device or remote controlled system with useless messages isn't a "best practice" - the question becomes how to generate the equivalent of a gamepad event without flooding the network or stalling the browser in a wait-loop.

Webworkers was a thought, but they cannot be used because they don't have access to the window.event context and cannot interface with the joystick/gamepad.  At least not directly.

In order to handle this efficiently, some method of triggering an "event" that allows data to be sent, only when something of interest happens.


Solution

  • For the benefit of those who may be confronting this same issue, here is the solution I eventually implemented:

    =======================

    My solution:
    This solution is based on the fact that the gamepad's time_stamp attribute only changes when something happens.  (i.e. A button was pressed or a joystick axis was moved,)

    1. Keep track of the gamepad's time_stamp attribute and capture it on the initial gamepad connected event.
    2. Provide a "gateway" condition that surrounds the routine that actually sends the data to the receiving device.

    I implemented this in two steps as noted above:

    First:
    When the gamepad connects, I immediately capture the time_stamp attribute and store it in a variable (old_time).

    window.addEventListener("gamepadconnected", (event) => {
        js = event.gamepad;
        gamepad_connected();  // Gamepad is now connected
        old_time = gopigo3_joystick.time_stamp  // Capture the initial value of the time_stamp
        send_data(gopigo3_joystick)  // send it to the robot
    

    Then I do whatever looping and processing of data I need to do.

    As a part of that loop, I periodically attempt to send data to the server device with the following code:

    function is_something_happening(old_time, gopigo3_joystick) {
        if (gopigo3_joystick.trigger_1 == 1 || gopigo3_joystick.head_enable == 1) {
            if (old_time != Number.parseFloat(jsdata.timestamp).toFixed()) {
                send_data(gopigo3_joystick)
                old_time = gopigo3_joystick.time_stamp
            }
        }
        return;
    }
    
    function send_data(gpg_data) {
    // this sends a gamepad data frame to the robot for interpreting.
        [code goes here];
        return;
    }
    

    The first function, is_something_happening, tests for two qualifying conditions:

    Both conditions must be satisfied, otherwise the test falls through and immediately returns.

    Only if both conditions are met does the send_data() function get called.

    This results in a stable interface that always gets called if something of interest has happened, but only if something of interest has happened.

    Note:  There are keyboard commands that can be sent, but since they have active events, they can call send_data() by themselves as they only fire when a key is pressed.