DOS Gaming In Docker
TLDR:
- Quick install Earthly.
- Run
earthly github.com/earthly/example-dos-gaming:main+doom
to start container. - Play DOOM at
http://localhost:8000
in browser.
Its been three decades since the height of the DOS era, and look how far we’ve come! A machine that used to cost $2,000 can be emulated - down to the processor! — in our web browsers while also checking email or watching a YouTube video. However, amidst these advancements, our old software falls by the wayside and stops working. Games are especially prone to this, since they often relied on incompatible tricks to eke out every ounce of performance from these old machines.
Many projects have sprung up to help preserve this heritage. DOSBox provides a modern, compatible environment for old games (and other software), while projects like the Internet Archive provide a massive library of DOS games, freely available to play in your browser. In my experience, they play pretty well!
However, I still miss those less-connected days of the early 90’s. I remember the thrill of riding my bike to the store to pick up a shareware copy of whatever game was available for $1. I then held a physical disk, with the game on it, ready to install on the nearest accessible computer. The self-contained nature of it all was magical. Nowadays, many web pages are bigger than the shareware games I used to buy.
This got me thinking: “Floppies can be imaged. That sounds kind of like a Docker image. I wonder… could I make DOS shareware Docker images?”
Turns out you can!
What You Will Need
- JS-DOS 6.22
- A copy of a Shareware game
- NodeJS
- Docker
- Earthly (Optional, but I think it’s neat!)
Putting It Together
First, we will need to acquire JS-DOS. JS-DOS is a wrapper around an Emscripten-compiled version of DOSBox, so it can run in a browser. You can get the latest versions of the files on js-dos.com. Download and place these files into a project directory. Here’s how I’m doing it, using Docker:
WORKDIR site
RUN wget https://js-dos.com/6.22/current/js-dos.js && \
wget https://js-dos.com/6.22/current/wdosbox.js && \
wget https://js-dos.com/6.22/current/wdosbox.wasm.jsRUN npm install -g serve
JS-DOS is nice, but it’s nothing without a game to play in it. For our example purposes, we’ll use one of my childhood favorites: Secret Agent.
❗ Warning
A couple things to note when you are looking for games:
- Make sure that the game you choose is actually shareware. Don’t copy that floppy!
- Make sure that it is the actual game files, not just the installer.
- While you can install and then run the game via DOSBox, you’ll have to install it on every visit to the webpage. The installation is also fairly slow.
- Make sure that it is a
zip
file. This is what JS-DOS wants to load for us.
Heres how I add the game to our image:
ARG GAME_URL
RUN wget -O game.zip $GAME_URL
Note
You may note that we don’t preserve the name of the downloaded file here. This is to make our job easier, when we make the game accessible to play later.
If you built and ran the Dockerfile at this point, you would have an image containing Node, JS-DOS, and a zip file of your beloved game; but no way to play it! To make it playable, we need to create a webpage which loads JS-DOS, loads our game, and provides a canvas for JS-DOS to render its output to. Here is my tiny webpage:
<html>
<style type="text/css" media="screen">
canvas {width: 800px;
height: 600px;
}</style>
<head>
<title>DOS Game!</title>
<script src="js-dos.js"></script>
</head>
<body>
<canvas id="jsdos" width="800" height="600" ></canvas>
<script>
Dos(document.getElementById("jsdos"), {
.ready((fs, main) => {
}).extract("game.zip").then(() => {
fsmain(["-c", GAME_ARGS])
;
});
})</script>
</body>
</html>
Note
GAME_ARGS
is the command for DOSBox (which is inside JS-DOS) to start once it is loaded. The CLI arguments should line up with what a regular installation of DOSBox would expect. If your game requires additional arguments, please provide them in a comma-separated list.
Copy this HTML file into the same directory as your JS-DOS files and your game. Now all you need to do is start a server within our Docker container to serve this webpage. For this, I used serve
, because it was quick and easy to script (you may have noticed installing this dependency alongside JS-DOS earlier). Heres how I add the server to the container:
ARG GAME_ARGS
COPY index.html .
RUN sed -i s/GAME_ARGS/$GAME_ARGS/ index.html
ENTRYPOINT npx serve -l tcp://0.0.0.0:8000
And now we have a shareable Docker container, with playable shareware inside! You can now play the game by:
docker build \
$ --build-arg GAME_URL=https://archive.org/download/msdos_festival_SCORCH15/SCORCH15.ZIP \
--build-arg GAME_ARGS=\"SCORCH.EXE\" \
-t mycool:dosgame .
...
docker run --rm -p 127.0.0.1:8000:8000 mycool:dosgame $
Going Further
Using Earthly, we can even go a step further! Earthly lets us separate some of the concerns within the Dockerfile:
About Earthly
Earthly makes creating Docker images easier. Take it for a spin!
jsdos:FROM node:16-alpine
WORKDIR site
RUN wget https://js-dos.com/6.22/current/js-dos.js && \
wget https://js-dos.com/6.22/current/wdosbox.js && \
wget https://js-dos.com/6.22/current/wdosbox.wasm.jsRUN npm install -g serve
game:FROM +jsdos
ARG GAME_URL
RUN wget -O game.zip $GAME_URL
web:FROM +game
ARG GAME_ARGS
COPY index.html .
RUN sed -i s/GAME_ARGS/$GAME_ARGS/ index.html
ENTRYPOINT npx serve -l tcp://0.0.0.0:8000
But it also lets us build and launch the game with a single command. It will also end up saving the game for you as an image on your local system! Here is the additional target that I added to accomplish this:
play:
LOCALLY
ARG GAME_TAG
WITH DOCKER --load jsdos:$GAME_TAG=+webRUN docker inspect jsdos:$GAME_TAG > /dev/null && \ #Using side-effect to save image locally too
docker run --rm -p 127.0.0.1:8000:8000 jsdos:$GAME_TAG END
We also have a couple pre-made targets that wrap this all up for you, and all you need to do is have Earthly installed! Running any of these commands will start the game. Just navigate on over to localhost:8000 and start playing! Additionally, you will find a Docker image on your system named jsdos:$GAME_TAG
for when you want to play later.
earthly github.com/earthly/example-dos-gaming:main+doom
earthly github.com/earthly/example-dos-gaming:main+secretagent
earthly github.com/earthly/example-dos-gaming:main+cosmo
You can run your own DOS games by running:
earthly \
--build-arg GAME_TAG=doom \
--build-arg GAME_URL=https://archive.org/download/DoomsharewareEpisode/doom.ZIP \
--build-arg GAME_ARGS=\"DOOM.EXE\" \
github.com/dchw/earthly-dos-gaming:main+play
Make sure you replace the tag, URL, and args as appropriate.
Conclusion
It’s neat that we can make independent, offline bundles, similar to those shareware floppy disks from back in the day. To see the project as a whole, check out the repository. And if you want a better way to build docker images and to build things in general take a look at Earthly. It’s pretty cool.
Thanks for reading!
Earthly makes CI/CD super simple
Fast, repeatable CI/CD with an instantly familiar syntax – like Dockerfile and Makefile had a baby.