Programming a C++ game for the web (Emscripten)
In this article I will share with you how I port and develop my games using C++ for the web. The simplest way to do that is to include emscripten into your project build system. Emscripten is a compiler suite for C++ that would take code compiled by clang to LLVM and then translate it into WebAssembly which is the new hot tech of the web. WebAssembly is highly performant and a lot of the popular engines like Unity are compiling to it when targeting the web as a platform.
If you like this content consider checking out my course on CMake where I cover this and much more.
Table of Contents
- Before you start…
- Installing emscripten SDK
- Project structure
- CMake settings
- Compiling without dependency management
- Compiling along with VCPKG
- Testing the compile file with a sample web server
- Conclusion
Before you start…
Before you start with this guide I hope you already have a solid knowledge on:
- How to write C++ code – you can start here or check some other sites to learn the basics of C++
- How to use CMake to build and structure your C/C++ projects. You can begin reading my CMake articles
- A game to port for the web. You can take a look at my “How to create a pong game” for a simple and quick example.
Installing emscripten SDK
To start developing for the web you would need emscripten SDK. You can take it from github using your favourite way (git clone, git submodule, git subtree or download as a zip) – https://github.com/emscripten-core/emsdk. After you download it to a location you need to execute the following command from a terminal in the emsdk directory:
emsdk install latest
Project structure
For the purpuse of the example I would assume the following structure for my project:
- vcpkg
- … for raylib …
- emsdk
- … for web …
- Pong
- src
- … game files …
- CMakeLists.txt
- src
- CMakeLists.txt
This is the project structure taken from my tutorial for programming a pong game. I like to keep all my dependencies within the project though it is not necessary to have emsdk folder relatively and you can have only one installment for your system. I also tend to use vcpkg for package management on my C++ projects – You can check more about how to manage dependencies with vcpkg in this article. Additionally on how to compile with emscripten I will also show you how to do so along with vcpkg.
CMake settings
I use CMake for managing C++ projects and as a first thing we need to define some additions to it specifically when compiling with emscripten. Emscripten will define some new flags that can be used that we will need to utilize for successful compilation. To recognize the emscripten environment in CMakeLists.txt we will have the EMSCRIPTEN varialbe set so we can do like so:
if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY")
set(CMAKE_EXECUTABLE_SUFFIX ".html")
endif ()
We set some C/C++ compilation flags that are:
- To use GLFW – this is one of the most popular window management libraries for different platforms. As you probably suspect the web doesn’t really have a window but emscripten itself has a version of this library that can handle some of the functions in GLFW like initializing a WebGL context inside a canvas element when window initialization is called.
- WASM – will make emscripten compile to WebAssembly (.wasm file). Emscripten doesn’t target it by default as of the writing of this article.
- ASYNCIFY – will make the WebAssembly code run on a separate thread than the UI thread of the web page.
We also set the executable to be .html so that except the .wasm module and the .js loading file we also get an example html page that loads them and in which we can see our application run.
Compiling without dependency management
In the example project structure I presented vcpkg because that is what I use but I will first provide the example of compiling the project without any dependency management systems. To do this you would like some way to tell cmake generator to use the compiler found in emsdk called “emcc” or “em++”. The simple way to do that is to generate the projects using the following commands executed in a terminal in the root of the project:
./emsdk/emsdk activate latest
emcmake cmake -S . -B build
The first line would provide the terminal with shortcuts to the current version of emscripten. In these shortcuts there is a program called “emcmake” that would add some options to cmake generator and select the correct compilers that cmake should use later on. Executing emcmake would just simplify you having to write down some full paths and will expand the command to something similar to this:
cmake -S . -B build "-DCMAKE_TOOLCHAIN_FILE=<fullpath_to_emsdk>/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
If you are using an IDE for C++ development that helps you automate the cmake process you may want to add the “-DCMAKE_TOOLCHAIN_FILE=<fullpath_to_emsdk>/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake” because it will understand it wihout you having to activate the latest emscripten SDK. The emscripten.cmake file will provide the relevant values to cmake.
To build the project after that you would just execute:
cmake --build build
After building you can look up how to test your compiled WebAssembly application using a simple web server.
Compiling along with VCPKG
If you are like me though you would probably want to use a package manager along with your C++ project setup. I personally use vcpkg which doesn’t provide ready made packages. You have to first install you package with the target platform in mind. Vcpkg would actually take a recipe of how to build this package and build it for that platform. Then you generally set the CMAKE_TOOLCHAIN_FILE to vcpkg so that it can find the packages that you want. The problem is that both emscripten and vcpkg use the CMAKE_TOOLCHAIN_FILE variable to assist compilation. I will show you how to comiple them successfuly.
Installing raylib for WebAssembly
First thing’s first. We need to compile our example dependency. Lets use raylib. Raylib added emscripten support for vcpkg not so long ago so at the time this article was written you would have to take the head version of the library. If you are using Raylib 3.5 which is the next version in line you’re probably already in the clear. To install for web you would need to first activate the emscripten SDK and then install vcpkg with the emscripten community triplet. You can do so by executing the following two commands from the root of the project:
./emsdk/emsdk activate latest
./vcpkg/vcpkg install raylib:wasm32-emscripten --head
Vcpkg should correctly download and install your package for the web.
Compiling the game with a dependency
The next problem is that to compile your project you would need to set both emscripten and vcpkg as a toolchain file. Luckly vcpkg provides another option called VCPKG_CHAINLOAD_TOOLCHAIN_FILE that will exececute another toolchain along with vcpkg. The cmake generation command would then look similar to this:
cmake -S . -B build "-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=<fullpath_to_emsdk>/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" "-DCMAKE_TOOLCHAIN_FILE=<path_to_vcpkg>/scripts/buildsystems/vcpkg.cmake" "-DVCPKG_TARGET_TRIPLET=wasm32-emscripten"
This would generate the project files for your web compilation. To build your project then you would simply execute:
cmake --build build
Keep in mind that not all dependenices in vcpk support the wasm32-emscripten triplet. This is still a community triplet and for example I had to go and implement some of the missing configurations in raylib to support it. At minimum you can find raylib and sdl2 to be working as well you can probably make your own engine compatible.
Testing the compile file with a sample web server
You would probably want to check out how your compiled application behaves in the browser. To do this you need a server because WebAssembly needs to be served. It’s not linked like javascript but loaded asynchronously from javascript – that is why you would also get an output of a .js file and .wasm file at minimum if you don’t set the executable extension to html.
Using emrun
The first approach would be to use emscripten’s built in server test tool called emrun. It can be found along the compilers in your emsdk – emscripten folder.
To start it we would open up a terminal with latest activated emscripten version after which we can run the following command to run the webserver using our generated html page:
emrun Pong.html
This will nicely start a server and run your built html file for testing.
Using Python3
If you have python installed (and its really easy to install python3) you can write and run the following script file in some directory and copy the output html, js and wasm files in the same directory:
# Python 3
import sys
import socketserver
from http.server import SimpleHTTPRequestHandler
class WasmHandler(SimpleHTTPRequestHandler):
def end_headers(self):
# Include additional response headers here. CORS for example:
#self.send_header('Access-Control-Allow-Origin', '*')
SimpleHTTPRequestHandler.end_headers(self)
# Python 3.7.5 adds in the WebAssembly Media Type. If this is an older
# version, add in the Media Type.
if sys.version_info < (3, 7, 5):
WasmHandler.extensions_map['.wasm'] = 'application/wasm'
if __name__ == '__main__':
PORT = 8080
with socketserver.TCPServer(("", PORT), WasmHandler) as httpd:
print("Listening on port {}. Press Ctrl+C to stop.".format(PORT))
httpd.serve_forever()
Lets say for the purpuse of the example that you named the file wasm-server.py you can then run it using the following command:
python3 wasm-server.py
Then you can open up the browser and your game should load. You also have a handy console window on the bottom and if there are any errors you will receive them on the top of the window.
Conclusion
Targeting the web is not that hard and its a great platform for some small games. Especially if you’re indie you can get a lot more trust from people and build a highly played portfolio of games if they can be played without downloading and installing.
Next on C++ for the web
You can check out how to debug your compiled executable in the next article.
Note: There is a newer article on how to cross-compile using dockcross
If you found this tutorial useful check some of my other tutorials on:
Leave a comment