Growing up in the 90ies I played a lot of games. Mostly on PC and often not on the most advanced PC, so adventure games were one of my favorite genres. Now being in a different stage of live, I have gained an apprecition for the work preserving games from past periods.

One way to mkae sure these games are preserved for future generations is to remove any friction involved in playing them (trust me - you don’t want to have to install Windows 95 or DOS in 2021).

For tis reason ScummVM has always been close to my heart. Initially meant as a modern implementation (“VM”) for the SCUMM language early lucas arts adventures were implemneted in, it has grown to support hundereds of games, including 3D games.

  • Preservation of classic games: ScummVM has played a critical role in the preservation of classic adventure games, ensuring that they remain accessible and playable for future generations. Many of these games were designed for outdated hardware and operating systems, making them difficult to run on modern computers without the aid of an emulator like ScummVM.

  • Cross-platform compatibility: ScummVM is designed to be cross-platform, allowing users to play these classic games on various operating systems such as Windows, macOS, Linux, and more. This cross-platform support ensures that users can enjoy these games regardless of the device or system they are using.

  • Enhanced gameplay experience: ScummVM not only enables these classic games to run on modern systems but also often improves the overall gameplay experience. It can provide features such as improved graphics, enhanced audio, and other quality-of-life improvements that were not possible when the games were originally released.

Loving the web, the natural next step is to make these games available on the web. The lesser friction the better, so let’s port ScummVM to the web!

Previous Work

Before I started, I actually did some research and it turns out people have been looking into this before, going back to 2013 with the introduction of asm.js. I’m not going too deep into details, but there were two previous attempts worth crediting1:

  • Jukka Jylänki - the Co-lead on the Emscripten project at Mozilla - built a proof of concept in June 2013 - the page is not online anymore, but the source code still available at .

  • Angus Lees - a ScummVM contributor - built a separate demo also in Jun 2013, still playable in 2021. I don’t think the source code is available for this.

Demo

First things first, let’s get to the point: This is an initial version of my Emscripten/Webassembly target as mentioned on the ScumMVM Discord a few days ago:

You can now try it yourself at scummvm.kuendig.io/scummvm.html including some freeware games, some demos and the ScummVM testbed 2.

As Emscripten has matured quite a bit in the last 8 years, I want to go beyond the demo and am actually plannnig to upstreaming this as a proper ScummVM port: DISTS: Initial Emscripten Demo / Proof of Concept

What works and what doesn’t

This port of ScummVM has two primary use cases as its goals:

  • Demo App: The goal of this use case is to provide an easy way for people to discover ScummVM and old adventure games. Game preservation is not just about archival but also accessibility. The primary goal is to make it as easy as possible to play any game which can legally be made available, and there’s probably nothing easier than opening a webpage to do so.

  • ScummVM as a PWA (progressive web app): There are platforms where native ScummVM is not readily available (primarily iOS/iPadOS). A PWA can work around these limitations. To really make this work, a few more features beyond what’s in a Demo App would be required:

    • Offline Support: PWAs can run offline. This means we have to find a way to cache some data which is downloaded on demand (engine plugins, game data etc.)
    • Cloud Storage Integration: Users will have to have a way to bring their own games and export savegame data. This is best possible through cloud storage integration. This already exists in ScummVM, but a few adjustments will be necessary to make this work in a PWA.

Not all of it required for this works, but we already have quite a few things:

  • All engines compile (though I didn’t test all of them), including ResidualVM with WebGL acceleration and shaders.
  • Audio works and 3rd-party libraries for sound and video decoding are integrated.
  • Proof of concept integration with BrowserFS to download game data lazily when required and to support local savegames.

Some ideas for possible improvements:

Emscripten Optimizations

Emscripten offers a ton of optimizations, not all of it works yet:

  • Optimize asyncify behaviour (we only have SDL functions calling wait currently), e.g with SDL_HINT_EMSCRIPTEN_ASYNCIFY.
  • Specify a ASYNCIFY_ONLY list to to make asyncify only instrument functions in the call path as described in emscripten.org: Asyncify
  • Don’t use asyncify but rewrite main loop to improve performance
  • Shrink code size or execution speed with -Os or -Oz emcc arguments.

Storage Integration

Emscripten out of the box only emulates a in-memory filesystem. My initial hack This is an initial version of my Emscripten/Webassembly target as mentioned on Discord a few days ago (see video https://discord.com/channels/581224060529148060/581224061091446795/849353211877523536)

static engine and bundling the game via a preloaded data file works. that’s what’s running in the video and also what the proof of concept in 2013 used. if that’s a goal for the project I can clean it up and push a PR fairly soon (I was planning on using dropbox/gdrive for game+save data and dynamic plugins - but that’s a lot more work)

For ScummVM to work properly, we will have to be able to load additional data from somewhere. I picked https://github.com/jvilk/BrowserFS. Unforutnately it’s pretty much abandoned (last release in 2017) and lacks a few things I’d like to have. 3

  • BrowserFS seems abandoned and never did a stable 2.0.0 release. Maybe there’s a better way to handle storage?

  • File loading improvements:

    • Load assets with HTTP Range request headers.
    • Load assets asynchronously (not blocking) via a worker.
  • Add support for save games (and game data?) on personal cloud storage (Dropbox, Google Drive).

UI Integration

  • Responsiveness: Adjust the canvas size when resizing the browser.

  • Bug: Fullscreen mode doesn’t work.

  • Build a nice webpage around the canvas.

    • Allow hiding of console, replace buttons/checkboxes from default emscripten template.
    • Bonus: Adapt page padding/background color to theme (black when in game)
  • ScummVM shouldn’t be able to “close” (there’s no concept for that:

    • Remove “exit” buttons from all menus.
    • Change any programmatic “exits” to cause a restart of Scummvm (or refresh of the page).
  • Pass CLI parameters for ScummVM via URL parameters to allow for “deep-linking” to a specific game.

Other Bugs + Tasks

  • Bug: Vorbis support is broken - parts seems to have been patched out so -lvorbisfile triggers an error during configure (and emscripten-core/emscripten#9849 doesn’t seem to fix this).
  • Bug: Going back to main menu from Grim (and other Residual Games?) messes up the render context and the UI is unusable.
  • Check all disabled features (e.g. TiMidity++) and see if they could be enabled (some might never make sense, e.g. anything requiring MIDI Hardware, Update Checking etc).

I’m currently working on a few improvements for the port which I hope will make it possible to release it as a general website with one of the upcoming releases. Mostly fixing the performance and adding support for cloud storage. My goal with this port is to make these games much more accessible, so ideally we can provide a playable version of the demos website one day :) Initially just as a experiment to see if I could make it run, I decided to eventually get this upstreamed. I don’t have much time to work on this, but I’m trying to keep this a continuous involvement (even if it just ends up 1-2 larger MRs per year).

How to build it

There’s still a lot to do depending on how such a build would be deployed. I summarized quick build instructions and a summary of what’s done and what’s still missing at ./dists/emscripten/README.md.

The linked demo above is built with this (running ./dists/emscripten/build.sh all). I only tested building on macOS, but at least in theory this should also build on Linux and WSL (or anything else that can run shell scripts and is supported by emsdk).

About Webassembly and Emscripten

Emscripten is an LLVM/Clang-based compiler that compiles C and C++ source code to WebAssembly for execution in web browsers.

Note: In general most code can be cross-compiled to WebAssembly just fine. There are a few minor differences, but the major difference comes down to how instructions are processed: JavaScript and WebAssembly support asynchronous/non-blocking code, yet everything ultimately runs in the same event loop. That means WebAssembly code has to pause so the browser can do its work—render the page, process inputs, run I/O, and so on. One consequence is that the page is not re-drawn until the WebAssembly code yields to the browser. Emscripten provides as much tooling as possible for this, but there’s sometimes still a need to manually add a call to sleep in some engines.

For ScummVM, the Emscripten target provides a script to build ScummVM as a single page browser app.

Emscripten is an LLVM/Clang-based compiler that compiles C and C++ source code to WebAssembly for execution in web browsers.

How to build for Webassembly

This folder contains a script to help build scummvm with Emscripten, it automatically downloads the correct emsdk version and also takes care of bundling the data and setting up a few demo games.

Running emscripten/build.sh

emscripten/build.sh needs to be run from the root of the project.

./dists/emscripten/build.sh libs|configure|make|data|dist|all|clean

It accepts a single parameter with 7 valid commands:

  • libs: Download and compile the required 3rd-party libraries required to build certain engines (libmad, a52dec etc)
  • configure: Run the configure script with emconfigure with the recommended settings for a simple demo page
  • make: Run the make scripts with emmake
  • data: Download some demos and set up all data require for the demo page
  • dist: Copy all files into a single build-emscripten folder to bring it all together
  • all: Run all of the above commands
  • clean: Remove all object files, built libs, bundled data etc

Independent of the command executed, the script sets up a pre-defined emsdk environment in the subfolder ./dists/emscripten/build.sh


  1. If you want to look into it more, there’s there’s been quite a bit of history on the ScummVM forums: https://forums.scummvm.org/viewtopic.php?p=76796 (2011) - and somebody even tried porting ScummVM manually to Javascript in 2010: . ↩︎

  2. Source code available at . ↩︎

  3. Considering the hacked together nature of the other ports I obviously also checked what they did. None of them having JS code to implement the filesystem code, I initially though Emscripten might provide something I missed. Helpfully angus lees confirmed that he indeed wrote his own filesystem code that just backs on to http range requests (with a cache). ↩︎