Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Playing with the Gamepad API

Posted on Oct 24 • Originally published at alvaromontoro.com I am not a gamer. Maybe I was at once in my life, but not anymore. Even then, I was not that great of a player. But two things happened that encouraged me to start using Gamepads on my computer. The first was the nostalgia of playing 90s/early 2000s video games, which drove me to purchase a set with a Raspberry Pi, a case, and a few controllers. Building a RetroPie became a personal project.The second one was an afternoon of "boredom." I wanted to develop, but I was running out of ideas. So, I decided to explore something new. I navigated to the Web APIs page on MDN, and something caught my eye on the letter g: Gamepad API.A standard API to access and control gamepads? And supported by all major browsers even when listed as "experimental?" Unexpected... but interesting. It caught my attention and my curiosity.I had a computer, a game controller, JavaScript knowledge, and a few spare hours... What did I have to lose?After reading the first page, it looked simple: it only had a handful of interfaces, events, and methods to play with. Nothing that I couldn't handle... or so I thought. I jumped to the MDN tutorial and simplified the first code example a little:A bit of theory: The gamepadconnected event is triggered when a gamepad is connected to the browser. Similarly, there is a gamepaddisconnected event that is triggered when a gamepad is disconnected. And with this paragraph, you've learned about the only two events available on the API. Now back to our story.The page loaded on the browser, and I giddily plugged the RetroPie controller into my computer and...Nothing.Unplugged the gamepad. Replugged it, and...Nothing.Unplugged. Replugged.Nothing.When I was going to return the gamepad to its rightful position by the console and move on to something else, I pressed some of the buttons, and something happened. A message showed up on the console:I reloaded the page, pressed a button, and the "Gamepad Connected" message popped up again on the console. I learned the first lesson of many with the Gamepad API: not all controllers connect to the browser as soon as they are connected to the computer. Most of them only activate after pressing a button or moving the joysticks.Now that I knew Chrome, the browser I was using, supported the Gamepad API, my next step was to test it with different browser operating systems. I tried on a Mac and a Windows machine, different browsers, and the same browser on different OSs, and for an experimental API, it is widely supported. It even runs on Edge on Windows!Some features may even be available in previous versions. Still, all the ones mentioned in this article need the browsers shown in the table above (except the vibration, for which support is inconsistent, as we'll see in a little bit).The next thought was: which controllers would work with it? I had tested with a knockoff gamepad that came with the RetroPie, but I had controllers for PS1, PS2, PS3, and Xbox One. (In hindsight, I have too many consoles around for someone who claims not to be a gamer.) Would the original console controllers work, too?Short answer: Yes.Long answer: Some do, some don't. For example, I didn't have problems with the PlayStation controllers (independently of the version) or with the Nintendo Switch controllers. Some friends tested their Wii controllers with a demo page, and they worked like a charm, too. Xbox controllers were a different story. It may be because they need more power; it may be that the versions that we tested were not correct. But we were unable to make any of them work....This is interesting, considering all the knockoff gamepads worked great, albeit with some caveats I'll explain later.The next step was expanding the example and exploring the Gamepad interface. Knowing that the gamepadconnected event passes the connected gamepad information as part of the parameter to the callback function. I logged that object so I could see its content:I was expecting something that matched the definition of the Gamepad interface:But the result came up with some additional information that looked promising:In which:There were some things that I still didn't have clear: the connected gamepad showed ten buttons and two axis, but looking at the physical device, I could see 12 buttons and no axis. It was a bit weird. Soon, I'd find out why that happened.Meanwhile, I was familiarizing myself with the gamepad interface and was ready for the fun part.I could detect a gamepad being connected/disconnected and read its values and properties. But that's not practical in itself. I wanted to move on to more exciting things.We just saw that the Gamepad object has a buttons property that contains an array of buttons. These buttons have their own interface (GamepadButton) which is an object with three read-only values:They are more or less self-explanatory:The buttons are sorted in the array by order of importance as defined in the diagram below so that they can be easily mapped:But not all gamepads follow the same button/axis pattern. That is why it is essential to know about the button mapping.mapping is a property of the Gamepad interface that indicates if the browser can identify and map the controller correctly. If that's the case, the value of mapping will be "standard."Most original controllers I tested and worked on had a standard mapping. Most of the knockoffs that I tried didn't have a standard mapping. In those cases, the developer must ensure that the pressed buttons match the user's expectations.Trick: Sometimes (e.g. when using a PS2 controller) the browser will detect that the gamepad is standard, but the buttons will not match the standard diagram. If that happens, make sure that the controller's "Analog" functionality is activated.Something was missing, though. I couldn't see any event being triggered when one of the buttons was pressed. Not in the documentation (where only two events are listed), nor on the gamepad object. Time to continue reading the tutorial and documentation.This was one of the things that took me a little bit longer to understand. We have already seen that there are only two events in the Gamepad API definition (gamepadconnected and gamepaddisconnected), and buttons don't have any events associated with them... So, how do the events work?Simply put, they don't... because there aren't any events. In contrast to other APIs and elements you can associate and listen to events, the Gamepad API works differently. Without any events to listen to, developers must continuously query the gamepads to see if any changes have happened.To achieve this, there is the getGamepads method as an extension of the Navigator interface. getGamepads will return an array with snapshots of the connected gamepads and their status:Later, to support some older webkit browsers, I added a fallback to an older initialization. Either way, if the getGamepads() methods are not supported, or the gamepads were disconnected, returning an empty array is a good idea to avoid errors:I could read the status of the connected gamepads, but it was a snapshot of the status from when I called the function. I needed to be querying the status of the gamepads continuously! Instead of using something like setTimeout or setInterval that could skip animation cycles, I had to call functions within requestAnimationFrame so that it was executed every time the browser was about to repaint the screen......something like this:This function would be called on the gamepadconnected event handler to start querying only when there's a gamepad connected to the browser. Also, it is essential to add a stop condition if no gamepads are connected. Otherwise, the app's efficiency will suffer by performing continuous unnecessary queries.Another discovery for me was how directional buttons (joysticks or axis) work. I must admit I was expecting them to behave like buttons, with pressed/not pressed, touched/not touched... but the axes property in Gamepad is just an array with an even number ranging from -1.0 to 1.0. Not even close to how the buttons look.The trick is to divide that array into groups of two. Each group will be an axe/joystick in the gamepad:Translated into code, it would be something like this:Beware: While everything seemed to work fine on my side, some people reported that the demos were not working. After some tests, the culprit was the browser: Firefox detects an additional axis, and you will have to make up for that!A nice thing to do while developing for a joystick/axis is to allow different sensitivity thresholds. Not all joysticks are created equal, and not everyone has the same likings or needs for how a joystick must behave.The value for the axis is a double, ranging between -1.0 and 1.0, but that doesn't mean that 0.0 is the at-rest status and 1.0/-1.0 is active. The rest status was never 0 in any of the gamepads that I have tested. (Most of them have a negligible value like 0.0003.) So, why must 1.0/-1.0 be the threshold that triggers the directional action?For accessibility and usability reasons, consider allowing users to change the threshold in which the directional event is triggered. Modified snippet from the example above:The Gamepad API has an extension to allow controller vibration when available. If the API in itself is experimental, this extension could be considered experimental to the square.If you looked into the console message logged when the gamepad was connected, you might have noticed a property that was not described as part of the Gamepad interface: vibrationActuator. That has a method playEffect() that will allow you to make the game controller vibrate.It's just that there's a big problem: that is not the standard extension to control the vibration, but rather the one that is available on Chrome. The standard way is using hapticActuators, which is available in some other browsers, notably Firefox.For this example, I will focus only on the standard hapticActuators.hapticActuators only allows one value at the moment ("vibration") and contains the pulse method that will permit us to trigger vibration specifying intensity and duration:One tricky thing about the hapticActuators is that when I found it, instead of being an array of GamepadHapticActuators as defined in the standard, it was a single object of that type. Implementation is still really dependent on the browser. Developer beware.As you may have noticed, the Gamepad API is relatively easy but cumbersome, as every action needs multiple steps. If I wanted to explore and play with it more, I would need to simplify the experience.Creating a small module to provide a higher-level interface to all these methods and events made sense. Something that would simplify every action and allow for more standard-looking calls.For example, if I wanted to check if the Start button was pressed, I had to do this with the Gamepad API:Each of those steps would take several lines of code. Moving that complexity to a library/module would allow us to do something similar in a simpler jQuery-ish looking kind of way:All the code needed would still be there, but behind the scenes, facilitating the use of the Gamepad API and making it look like other APIs in which events are listened to instead of queried.The library simplified the process. Now, I could focus more on the other parts of the web applications while using a friendlier interface in JavaScript for the gamepads' functionality.An easy game to develop was the classic Pong. The interaction with the controllers is simple: up or down. I just had to calculate the ball's movement while dealing with the main difficulty of detecting the collision of the ball with the paddle.Here is the code and a demo (connect your gamepads to play):If you don't have gamepads connected to your computer, that Codepen may not work, although I added some keyboard functionality, too.In this article, we have touched on most of the current features of the Gamepad API:But we left some things out: the Gamepad Pose interface, which would allow us to get information from gamepads such as position, orientation, velocity, and acceleration (if available). This would be great for augmented reality and virtual reality devices. Unfortunately, it's not well supported.Also, new changes will come to the API. After all, it is an experimental technology, and it is continuously updated. New events could be added, such as gamepadchange, gamepadaxischange, which would simplify the API... and make my library obsolete.After testing the different types of controllers that work with the Gamepad API, I had an idea: if my old PS3 controllers worked, how about my old Rock Band drums and guitar? How about the Dance Dance Revolution mat?Long story short, these are the results:...and here we are playing our own version of "Web DDR":I created a tutorial on how to make your personalized version of a Rock Band video game using JavaScript and HTML in this other article.Templates let you quickly answer FAQs or store snippets for re-use.This is very cool. I look forward to seeing what web games people come up with using this library. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse Vinish Kapoor - Oct 21 Patrick - Oct 23 Necati Özmen - Oct 23 Sultan - Oct 23 Once suspended, alvaromontoro will not be able to comment or publish posts until their suspension is removed. Once unsuspended, alvaromontoro will be able to comment and publish posts again. Once unpublished, all posts by alvaromontoro will become hidden and only accessible to themselves. If alvaromontoro is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to Alvaro Montoro. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag alvaromontoro: alvaromontoro consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging alvaromontoro will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Playing with the Gamepad API

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×