Asteroid shooting fun with Blast!

Pushing Phoenix LiveView to the limit

Josh Price Portrait

Josh Price

11 November 2021 · 3 min read

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.

Josh Price Portrait

WRITTEN BY

Josh Price

I love building and designing software. When I'm not building awesome software I'm probably BBQing or bushwalking.

More articles

TypeScript Logo

TypeScript

Why we use TypeScript vs JavaScript

Josh Price Portrait

Josh Price

24 November 2021 – 5 min read

Git Logo

Git

The identity of a Git commit explained

Kathryn Prestridge portrait

Kathryn Prestridge

15 November 2021 – 5 min read

Want to read more?

The latest news, articles, and resources, sent to your inbox occasionally.