eieio.games

by nolen royalty

Infinite Hamlet

Typing Hamlet one character at a time into twitch chat

Aug 2, 2023

I turned off Infinite Hamlet on September 30th.

Some quick stats: 41 unique chatters submitted 4,400 characters and completed 136 lines of hamlet. Running it was a ton of fun! I’m excited to revisit this kind of game in the future.

Basics

  • Genre: twitch-plays-pokemon-like
  • Links: play it live on twitch
  • Engine: Godot 3.5
  • Time Spent: ~2 weeks on and off
  • Other Stuff: I built this game at Recurse Center, a magical place that functions like a writers retreat for programmers. Consider applying!

Gameplay Video

To play Infinite Hamlet you attempt to type out all of Hamlet one character at time via twitch chat. That’s the whole game. It’s called “Infinite Hamlet” because of the infinite monkey theorem

Riveting

High level thoughts

This game is not particularly fun when you are playing it on your own (it’s sort of fun if you’ve got a few people in twitch at the same time).

That’s ok! It’s a silly game that has caused several of my friends to inadvertently chat with each other. I like that I can leave it up for a few months as an experiment. I enjoyed building it and now that I know the ins and outs of building a twitch game I think I can build something more interesting. Or more fun.

Why did you do this

For a while I’ve been interested in designing games to be played via Twitch chat. Twitch plays Pokemon was great (!) - but it involved taking an already existing game and hooking it up to chat. Some games now feature Twitch chat integration - chat viewers can “visit” a streamer’s restaurant in PlateUp or queue up random events in Noita. But those integrations are typically about making a game more appealing to stream on twitch.

What if a game was instead designed to be played on twitch? What would that look like?

A few weeks ago for a Recurse Center “impossible day” event 1 I built a crappy prototype of one idea: an infinite runner where instead of controlling the runner you generated terrain in front of the runner - you could enter ”-” in chat to generate flat terrain or ”/” to generate rigid terrain 2.

1

An event where you push yourself to take on a task that feels totally insurmountable in a single day.

2

The code is here but doesn’t currently include the bot in charge of listening to chat and forwarding on messages.

Imagine playing this on a 5-second delay!

The game was kinda neat but I found that the delay between chat and the stream was long enough (a few seconds) that it wasn’t a great fit: it’s just too hard to react to anything in time.

I spent a while brainstorming games that would be a better fit and was most interesting in games where the content on screen was mostly or entire user generated - e.g. games where you could draw shapes or even write code via chat. But I struggled to come up with a game idea that was:

  1. Exciting and flexible enough that I was interested in making it.
  2. Not trivial to use to write / draw hate speech.

Infinite Hamlet was a straightforward way to take care of (2). It definitely gives up a lot of (1) - but the theme was funny enough to me that I was excited to make it anyway. And I figured that plenty of the things that I learned while making this game would be generalizable to future twitch games.

So how does this work?

This game has 3 components

  1. A Python bot that consumes twitch chat.
  2. A Python bot that emits sounds when a word is completed.
  3. A Godot game that renders what you see on stream.

The Python bot authenticates to twitch chat (which is just IRC after you do an OAuth dance). When it sees messages that look relevant it sends a little UDP packet containing the character that was typed along with some metadata (like who typed it) locally.

Our Godot game is listening for those UDP packets - when it sees one it validates and decodes it and then performs some actions (updating scores, adding a new character to the screen, etc). If the character results in us finishing a word the Godot game sends a little UDP packet containg that word.

A separate Python bot is listens for UDP messages on the port that Godot sends packets to. When it gets a word it feeds the word to pyttsx3, a lovely offline Python text-to-speech library.

A diagram of the system. A box named 'twitch chat' connects to an 'irc bot', which feeds into 'godot.' Godot points to 'tts bot' and 'OBS'.

Listen, there are at most two extra boxes here.

It’s a little funny and…microservicey(?)…to have 3 separate programs like this, but I ended up really liking the approach. It doesn’t add much configuration overhead for me (that time is dwarfed by actually setting up a stream for the game) and I find it much easier to think about. And I simply find it so much easier to write code that interacts with the outside world in Python than in GDscript, so I think I saved a ton of time this way.

One funny problem that comes up is that when I re-OAuth I open up Chrome, which steals focus from the Godot window running the game. This can result in some of the game’s animations not playing at a high frame rate (since it’s in the background). To handle this I wrote an Autohotkey script that re-focuses the Godot window and trigger that script via pyautogui after the OAuth process is finished. Hacky but hey - it works fine!

But how is this hosted?

So getting this running with the microservice-y approach I described above was actually pretty straightforward - but I was running the game from one of my own computers.

I…don’t want to do that! Not only is the idea of having an always-on Twitch stream running out of my home creepy, but there’s no good story for managing the stream when I’m not at home (I am not excited about setting up remote access for my home computer).

And getting this running on a server wasn’t totally trivial! I am plenty used to spinning up servers to run things, but I’ve never spun up a server in order to run (and stream!) graphical applications before.

The first thing that I tried was spinning up a small Windows box on Azure. I figured it’d be easier to run and stream my game from Windows than Linux, and I also anticipated that doing the naive thing wouldn’t work. That was exactly right - Godot refused to start because the box didn’t have a graphics card (well specifically because the video card driver didn’t support Godot’s minimum required OpenGL version).

But it’s hard to find a server from the big VPS providers without spending a ton of money! Most GPU-oriented instances are super beefy and cost hundreds of dollars a month 3. This game…does not need a beefy GPU instance! Have you seen it. It’s some text on screen. It just needs some video drivers.

3

It’s possible that some non-GPU oriented instance on Azure or GCP or AWS would be able to run my game but there are so many instance types that I wasn’t excited to do a bunch of testing.

I floated this question to Recurse Center’s internal chat and Erika pointed me to Vultr, which has some fractional GPU offerings that clock in at at around $30 instead of $300.

I burned several hours trying to get my setup functioning on various Linux boxes before bailing. Godot refused to start on Vultr’s stock Debian and Ubuntu images no matter how much tweaking of the video drivers I did. Godot ran out of the box on Vultr’s Arch-based “3DKit” machine but OBS segfaulted on startup (I was surprised at this since it was pre-installed!). I eventually worked around this by changing the installed video driver only to find that OBS segfaulted as soon as I tried to stream.

A screenshot of some intimidating terminal errors while trying to start Godot.

For the record, this was with LIBGL_ALWAYS_SOFTWARE set.

I’m too old to spend more than 4 hours trying to configure Arch, so I bailed and spun up a Windows box and spent exactly 0 seconds fighting with my drivers. Exciting! The primary downside of my Windows-based setup is that I need to maintain a constant RDP connection to the box or the screen locks 4, which prevents OBS from streaming. Right now I just do this from a computer at home, but I plan to migrate that to a linux server that’s RDP’d into the Windows box in a screen session.

4

I think this is configurable if you pay for a Windows server license but I’m not excited to do that.

Wrapping up

I enjoyed building this game and sharing updates about it as I built it - it’s silly and different and very accessible as long as you’ve got a twitch account. And since it also served to clear up some things for me about building a twitch-first game I’d consider it a success.

But it also made me miss more traditional games. I’m excited to build a more typical game next and to come back to twitch-first games later with a fresh set of eyes.

Thanks for reading!

Keep up with me on my socials 👆

Or sub to my newsletter here! 👇