Realtime Games for the Web with WebRTC and Hasura GraphQL Subscriptions
We recently created a small game to drive engagement at one of our event booths. We needed to solve a couple of obstacles. The game needed to be broadcast onto a large screen to drive attention. There needed to be a way for an admin to interact with the requested games so that not everyone could just broadcast their gameplay. And everyone needed to bring their own controller. Yes, really.
Taking these requirements to the drawing board we came up with the following interface mechanics. A large screen would display the actual gameplay. Players would scan a code presented in a number of locations and be taken to a webpage where they could initiate a game request.
An admin would see the requested games, and could effectively “stage” a request on the big screen, which would let the game begin. Once the game was staged, the player’s controller would transform into an interface with some buttons to commence gameplay.
Architecting multiple clients together
From a technical point of view, the challenges of this workflow were far from solved. There’s a minimum of three actors – player, admin, and stage. They need to respond to interactions prompted by one of the other actors in the chain, and, in the case of the stage, do so autonomously unless we want to have someone literally running back and forth to accept staged game requests from the admin.
Fortunately, subscriptions were able to help us here. Creating a request to play created an insert into a game table. The admin interface was subscribed to these requests and could “accept” a requested game. The stage also had subscriptions and would use a small hack that would ultimately immediately respond with approval as soon as it saw a new “active game” request. Meanwhile, after creating the game request, the player would have subscribed to the status of their game request and see when the admin staged their game, and when the stage auto-confirmed the game, which would then let the player begin playing.
Latency in real-time web games
Managing the player/admin/stage mechanics with subscriptions works with very effort. But subscription latency simply won’t be fast enough to broadcast actual game-play mechanics between the player and the stage. For that, we need an ultra-low-latency web connection, and as of writing, WebRTC is the only game in town for that. In the future, the web Bluetooth API should make this work better for trusted sources, but generally speaking, don’t let untrusted devices connect to your machine with Bluetooth.
Understanding WebRTC high-level view
WebRTC is a basic protocol that establishes a connection through an offer/answer interchange in a format known as Session Description Protocol, or SDP. The requesting party sends a description of their network configuration as a proposal for how the two devices might find each other under current network conditions (usually looping through the local network hub). The responding device sends an answer with the same information, and the response also acts as a confirmation that the other party is wanting to connect.
Both of these offers and answers are created by using a lookup at an intermediary STUN server (or TURN if on mobile/secured networks) which ultimately provide a type of GPS for your device’s network path. It explains the path it took to get to and from your computer, and so, when compared between two devices, they can quickly find the lowest common node.
Many services provide free STUN servers, including Google. TURN servers are a bit more complicated as they handle more edge cases for routers that have different ports turned off (such as is the case for mobile data networks.) Twillio offers an affordable option to consider as a backup.
The shape of this response is in the ICE protocol (Interactive Connectivity Establishment). So, a network configuration conforming to ICE is sent and stored in a format known as SDP. Party one sends the first half (offer) and party two accepts by replying with the other half (answer). Of course, there’s lot’s more to be read on the topic of WebRTC elsewhere. For the visual learners, I personally found Hussein Nasser’s tutorial quite helpful.
Sending offers and answers
Interestingly, there’s no requirements in how the offer and answer need to be transported. As soon as an offer is created (and remains in memory) you can send this payload however you want! It could be through a text, email, bluetooth file transfer, etc. It’s up to you! And that’s where Hasura subscriptions become a helpful intermediary. The subscription latency is perfectly accessible to know someone is calling!
So our player device creates an offer, writes that payload as a JSON object to Hasura. Our stage device (when approved by the admin) is subscribing to the current “active” game’s offer, and wil auto reply with an answer. Meanwhile, the device is subscribed for answers and so the two are able to create a connection.
Storing gameplay data in Hasura
As we already have the tooling in place to use Hasura for creating and initializing our games, Hasura provides a very convenient method for us to get access to gameplay data as well. We are able to write data over the course of the game to our database such as which characters were chosen, and the score achieved which lets us access a running high-score and player list.
Building the game
As with building anything with Hasura, the actual Hasura plumbing is the fastest part to work with. Writing a game from scratch with very little prior experience, however, is not fast. Fortunately, we came across a fun library called Kaboom.js which lets you write canvas-based games in a functional way, which turned a quirky concept into an actual game, complete with Hasuraas, badies, and powerups.
The video below will give you an example of how the game came together.