In early 2019, when the world was blissfully unaware of COVID-19 and we could still do in person conferences, Alembic ran a full day Intro to Elixir workshop at LambdaJam in Melbourne.
We assumed our students had no prior Elixir knowledge and took them through the language basics, using the iex
REPL which towards the end of the day culminated in implementing some simple Phoenix LiveView apps.
As part of the workshop we built an Asteroids-style vector-based game using LiveView. This let students have a go at grokking a more complex application and see if they could add the missing features. It was great to see some of the attendees get super excited; for example we had one attendee try to add sound effects by rendering <audio>
elements to play sound effects. Someone else tried to reduce the size of the rendered SVG by using lower precision vector representations.
In summary we had a lot of fun!
Introducing Blast!
Blast is a multiplayer Asteroids-style game with real physics. The backend is a Phoenix LiveView app which renders an SVG representation of the game arena at an impressive 60fps. It's a testament to the power of LiveView that this can be rendered in real-time across the internet. Even more impressive is that it's currently deployed on a free tier instance at Gigalixir and can be found here.
How it hangs together
The heart of the game is the GameState
module. This is a struct representing the entire world with all active players, sound effects and projectiles, plus functions for updating it. This module is purely functional with no side-effects.
Wrapping that is a GenServer
called GameServer
. The GameServer
manages a single GameState
and updates the state of the world every 16 milliseconds. At the end of every 16ms server frame it uses Phoenix PubSub to post the state of the world to all subscribers. GameServer
is therefore not purely functional because it actually performs I/O and manages timing.
A LiveView
implementation called GameLive
subscribes to the GameState
messages sent by the GameServer
and renders an SVG. GameLive
is also a GenServer
and there is one instance of GameLive
per player connected to the game (Blast currently supports up to 4 players, which is a hard coded limit and could probably support more).
All of this orchestration is capable of delivering ~60fps to each person connected to the game although it gets janky across the internet - running on a local network is much faster.
Please don't write a real game like this
Modern multiplayer network games use various latency compensation techniques and tend to prefer UDP over TCP. The world simulation will run on both the client and the server so that the client can smooth out gameplay in adverse network conditions when packet loss or latency is an issue.
You don't get that with LiveView - the frontend is really just a simple client, meaning that the client only updates the display when told to do so by the server. It uses a websocket over HTTP - which is TCP underneath. That means dropped packets will cause a pause in rendering while the protocol stack recovers and the packets are re-transmitted in order.
Overall, it's impressive that Phoenix LiveView can produce a playable game at all. So it shows you what's possible with near realtime updating web applications with lower framerate requirements.
Hacking on Blast
If you're interested in checking out the source then feel free to check out the code here. Fork the repo, and PRs are welcome! There are a bunch of feature ideas documented in the README.
The code is a couple of years old now but feedback and PRs are welcome. We'll probably aim to update it to use the latest features of LiveView soon so stay tuned.