Porting TADS to the browser

This is one of my most recent projects (at least those that I get around to writing about…). I recently got infatuated again with Interactive Fiction, played through Ballyhoo by Infocom as well as took part in the judging of the Interactive Fiction Competition. In the course of this, I became more interested in how these games are made and am still blown away by how good results they were able to achieve back then (Google Asssistant and Alexa have nothing on those old interpreters apart from their voice recognition).

This lead me to playing around with the Text Adventure Development System (TADS) by Michael J. Roberts. TADS is one of the best-known toolkits to create interactive fiction and probably shares the top one spot with Inform.

Apart from starting to build a small game, I noticed that it doesn’t have a real native browser port. And I had never worked with emscripten before, so this seemed like a good opportunity. Emscripten is a cross-compiler which can compile C++ to javascript in order to run it in a browser, for example.

Process

In order to run TADS games, I looked at the available source code for TADS interpreters. TADS itself offers the code for the interpreter (see https://www.tads.org/t3dl/t3_src.zip), but to get the job done it was easier to look for a whole package in which everything required (handling the terminal, loading files, …), which I found with FrobTADS.

The next step was to find a compatible curses library in order to render the text to the browser. After some testing, I found PDCurses which comes with SDL1 and 2 backends, which in turn makes porting them using emscripten easy as SDL is one of the libraries that emscripten ships with.

In order to make both packages work, I had to make a few changes because clang, on which emscripten is built, was complaining about a few things in the code. Also the autoconf configuration couldn’t figure out that a curses library was present so I had to hardcode it in there.

One tricky thing was that emscripten complained about SDL_Delay being called, which will make the runtime stop. SDL_Delay is used inside PDCurses and then in turn used by FrobTADS as an implementation to pause the program. I solved this by using the Emterpreter, but I think that a cleaner solution would be to use emscripten_sleep() instead.

Result

You can find my fork of FrobTADS over on github: https://github.com/SpookyFM/frobtads. I added a fork of PDCurses as a submodule for convenience. This includes the shell (hardcoded to run the demo game) as well as the changes I made.

In order to build the whole thing, do the following steps:

For setting up your build environment: 
source ./emsdk_env.sh
To configure and build FrobTADS:
emconfigure ./configure CPPFLAGS="-I/PATH/TO/PDCurses-3.6"
emmake make

To build PDCurses, run inside PDCurses/sdl1:
emmake make

To build everything (Should have a .gam file in there as well):
emcc frob.bc /PATH/TO/frobtads-1.2.3/PDCurses-3.6/sdl1/libpdcurses.a -o new.html --preload-file pdcicon.bmp --preload-file pdcfont.bmp --preload-file *.gam --use-preload-plugins --shell-file shell.html -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1

To demonstrate the result, I have used the first winner of the Interactive Fiction award, Uncle Zebulon’s Will by Magnus Olsson. You can find the game playable in the browser here. Note that the download is actually quite heavy, for a text adventure, so give it a moment.

Possible next steps

At the current moment, this is more of a proof of concept than something ready to use. The above-mentioned SDL_sleep is one of the things that should be easy. Other things include:

  • testing if the curses library is doing everything it should do (some games can go crazy with doing 2D animations on the screen and such things)
  • supporting dynamically loading games instead of hardcoding them into the build
  • supporting save games and loading (possibly with html 5 web storage or cloud providers)
  • Increasing the resolution of the output and adjusting it to the browser window size