paint-brush
Play Pokemon on the Go — Porting a GameBoy Game to the Web Browserby@matejsmycka
532 reads
532 reads

Play Pokemon on the Go — Porting a GameBoy Game to the Web Browser

by Matěj SmyčkaNovember 7th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This text describes the process of making a legacy game to run in the browser via compiler Emscripten.
featured image - Play Pokemon on the Go — Porting a GameBoy Game to the Web Browser
Matěj Smyčka HackerNoon profile picture


Picture this: Taking a game as classic as Pokemon Red and making it run in your web browser. This text describes the process of making a legacy game to run in the browser via compiler Emscripten.

The code is executed on the client side, the main advantage being that no server is required, so it can be hosted for free on Gitlab or GitHub Pages.

This approach is truly cross-platform, making it possible to play the same game on any device and architecture that supports browsers.


Here you can try it yourself https://525073.gitlab-pages.ics.muni.cz/wasm-pokemon/

Source code: https://github.com/matejsmycka/gameboy-wasm-emulator


Pokemon

How it works

Old games need an emulator to be playable, which simulates the original hardware but as software on modern PCs.

Emulators can be written in any language. However, in order to make it run on the client side, it has to be in a language that the browser runtime can execute.

Historically, browsers could execute only JavaScript, but in recent years, all major browsers have implemented support for WebAssembly (WASM). Emulators, which are written in some compiled language like C, C++, Rust, or Go (Check out a list here), can be compiled to WASM. You can then hardcode the game into the emulator itself. So when you visit the website, JavaScript will load WASM binary with an embedded emulator and game ROM.

Step 1. - Choosing a compiler

There are quite a lot of compilers, however, I found Emscripten to be the best because it handles many things like automatic HTML and CSS generation for your WASM binary.


--shell-file /usr/lib/emscripten/src/shell_minimal.htm


You also can use many different templates.

Step 2. - Choosing emulator

There are a lot of limitations that will occur in porting, WASM has its own interface WASI, and unlike POSIX, you can't just read and write to files as you wish. All files that WASM runtime can access have to be bundled in the binary itself. It also cannot call any external libraries. Everything has to be included in compile time. Fun fact, you can't even open a network socket in the WASM. So, picking an emulator that is as lightweight as possible is crucial.


Another thing is that you have to choose a graphic library that the compiler supports. Emscripten supports SDL2 and Raylib, After trying multiple emulators, I chose GameBoy Emulator by David Jolly because it supported both libraries and was lightweight.

Step 3. Porting

In this part, you have to load the legally obtained ROM of the game directly to the code. I used a tool xxd, that translates the binary directly into a huge C array, it is ugly but reliable. Remove any ARGS that have to be provided. It is not feasible to launch the WASM binary with arguments, but it can be done in the javascript loader. Remove any features that are not mandatory for the emulator, like controller support.

Step 4. Compiling

This is the part where I spend most of the time; modifying all compiler options is tedious work, and I couldn't get the RayLib to work.

After a lot of time of googling and trying different options, I came up with the following command:


emcc -o build/index.html $(find src -name "*.c" && find tool -name "*.c") -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result -Os -I. -Itool  -I src/ -I src/system -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 -s FORCE_FILESYSTEM=1 --shell-file /usr/lib/emscripten/src/shell_minimal.html -DPLATFORM_WEB -s "EXPORTED_FUNCTIONS=["_free","_malloc","_main"]" -s EXPORTED_RUNTIME_METHODS=ccall -DCLIENT_SDL2 -sUSE_SDL=2 -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=32MB

It could be optimized like the header files could be imported automatically, the total memory is overkill, and some options might be redundant. After finishing the compile stage, the compiler will yield HTML, CSS, JS, and WASM files. Run python -m http.server to play the game locally.

Step 5. Tinkering

In this stage, you can alter the files above, like modifying the CSS to resize the play screen or modifying the HTML to alert the player how to play.

Finally, deploy the project with some free services, below is an example of how easy it is to play the game on GitLab Pages:

image: ruby:latest

pages:
  stage: deploy
  script:
    - mkdir public
    - cp build/index.wasm ./public/
    - cp index.html ./public/
    - cp index.js ./public/
  artifacts:
    paths:
    - public

Alert the user about controls

Resources

Thank you for reading, feedback is appreciated.