To build your raylib game for HTML5 you need a different compiler than the one that comes with raylib installation, actually, you need a set of tools. Those tools are the [emscripten SDK](https://emscripten.org/).
raylib C code can be compiled to [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly) to run on Web. Compilation process is very similar to the one used for desktop platforms with `gcc` compiler but it requires a different toolchain: [emscripten SDK](https://emscripten.org/).
### Installing emscripten
`emscripten` provides a set of tools to compile C code to WebAssembly, the main tool provided is the `emcc` compiler. `emcc` is actuallya direct replacement for `gcc`, so, anyone with experience compiling code directly in the command-line should not have much trouble to use `emcc`.
Download [emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) from [GitHub](https://github.com/emscripten-core/emsdk), download as a zip and decompress it in `C:\emsdk` folder.
There are some additional compilation flags for `emcc` compilation and code linkage, so, a `Makefile` is provided in [`raylib/src/Makefile`](https://github.com/raysan5/raylib/blob/master/src/Makefile) to simplify the compilation process, it only requires defining `PLATFORM_WEB` to use the correct compilation flags.
`emsdk` requires [**Python**](https://www.python.org/downloads/) and [**Git**](https://git-scm.com/downloads) installed and accesible from system path to be called from `emsdk prompt`.
The complete process to compile for web is detailed below. The main steps to follow are:
After decompression and installing required tools (and making sure `python` and `git` can be called from command line), go to `emsdk` installation folder and run `emcmdprompt.bat`.
1. Install emscripten toolchain
2. Compile raylib library
3. Setup raylib game for web
4. Compile raylib game for web
Note that it's VERY important to understand the different steps of the process. If you expect to find an already setup solution, ready to use out-of-the-box, it's very probable that it fails at some point. So, understanding the process is crucial to be able to configure web compilation with ANY build system.
## 1. Install emscripten toolchain
Download [emscripten SDK](https://emscripten.org/docs/getting_started/downloads.html) from [GitHub](https://github.com/emscripten-core/emsdk), download as a zip and decompress it in `C:\emsdk` folder. Those pages also provide detailed instructions.
`emsdk` requires [**Python**](https://www.python.org/downloads/) and [**Git**](https://git-scm.com/downloads) installed and accessible from system path to be called from `emsdk prompt`.
After decompression and installing required tools (and making sure `python` and `git` can be called from command line), go to `emsdk` installation folder and run `emcmdprompt.bat` (on Windows) or simply run the below commands from command-line on Linux.
Execute the following commands to install and activate latest emscripten tools:
```
emsdk update
emsdk install latest
emsdk activate latest
```
@ -21,21 +35,47 @@ On Linux and macOS you will also need to set the proper environment so that rayl
_NOTE: Updated installation notes are always [available here](https://emscripten.org/docs/getting_started/downloads.html)._
### Compiling raylib source code
## 2. Compile raylib library
Before compiling your game, raylib library **must be recompiled for HTML5**, generating `libraylib.a`.
Before compiling your game, raylib library **must be recompiled for HTML5**, generating `libraylib.a`. Make sure all paths to emscripten and tools are correctly configured, `emcc` should be accessible from command-line.
### 2.1 Command-line compilation
To compile raylib library directly from the command line, those are the commands to run:
The `-Os` flag is used to tell the compiler to optimize code for size, the `-Wall` flag enables all compiler warning messages. Some additional compilation flags can be used (actually provided Makefile defines some more) but they are not required.
The compilation will generate some warnings but it should compile successfully.
### 2.2 Using Makefile
* For Windows users :
Before compiling raylib, make sure all paths to emscripten (`EMSDK_PATH`) and tools are correctly configured on `C:/raylib/raylib/src/Makefile`, you must verify [these lines](https://github.com/raysan5/raylib/blob/master/src/Makefile#L149).
Before compiling raylib, make sure all paths to emscripten installation (`EMSDK_PATH`) and emscripten required tools (Clang, Python, Node) are correctly configured on `raylib/src/Makefile`, you must verify [these lines](https://github.com/raysan5/raylib/blob/master/src/Makefile#L156).
To compile raylib source code, just execute Notepad++ script: `raylib_makefile` and `SET PLATFORM=PLATFORM_WEB`. To do this, start up Notepad++ for raylib, open the `raylib.h` file, press F6, choose `raylib_makefile`, verify that in the script `SET PLATFORM=PLATFORM_WEB`, then click OK to run the script. That script just calls the following `make` line (in case you're are working on a custom environment):
From command-line, the following line must be called:
`make PLATFORM=PLATFORM_WEB -B`
`mingw32-make PLATFORM=PLATFORM_WEB -B`
NOTE: `mingw32-make.exe` is provided by MinGW toolchain, other compiler toolchains could provide similar implementations, usually called just `make.exe`. In any case, `make` must be accessible from command-line to execute it.
If you are using the provided raylib installer with Notepad++, it comes with a Notepad++ script ready to compile raylib library using makefile, the script configures required paths and calls required `Makefile`. To do this, start up Notepad++ for raylib, open the `raylib.h` file, press F6, choose `raylib_makefile`, verify that in the script web platof is set (`SET PLATFORM=PLATFORM_WEB`) and click OK to run the script.
* For Linux users :
* For Linux/macOS users :
Before compiling raylib, make sure all paths to emscripten installation (`EMSDK_PATH`) and emscripten required tools (Clang, Python, Node) are correctly configured on `raylib/src/Makefile`, you must verify [these lines](https://github.com/raysan5/raylib/blob/master/src/Makefile#L156).
You have to modify 3 variables :(`EMSDK_PATH`) (`PYTHON_PATH`) and (`PATH`). Just verify inside the emsdk directory if you have correct paths for (`EMSCRIPTEN_PATH`), (`CLANG_PATH`) and (`NODE_PATH`). (`EMSDK_PATH`) corresponds to path where you downloaded emscripten.
@ -61,11 +101,11 @@ _If you get "emcc: command not found" or a similar error when running `make` but
Generated `libraylib.a` is placed in `raylib\src\libraylib.a` directory.
#### Using CMake
### 2.3 Using CMake
If you prefer to use `CMake` instead of the plain `Makefile` provided, you have a few options to choose from.
##### Generating the build system files
* Generating the build system files
**You can go with the [emscripten suggested way](https://emscripten.org/docs/compiling/Building-Projects.html)**: That is to use the following command that will add the compiler and toolchain options for you:
@ -90,7 +130,7 @@ One note here - if you're using **vcpkg** for package management and have instal
@ -98,7 +138,7 @@ To build the project you would need to execute:
cmake --build build
```
...but keep in mind that you also have to add some additional setting in your CMakeLists.txt for emscripten. For the linker to execute successfully it will need the GLFW symbols which cannot be built by you. Luckily emscripten provides those symbols when you add the "-s USE_GLFW=3" to your linker. To do so you can add these lines somewhere in the root of your CMakeLists.txt
...but keep in mind that you also have to add some additional setting in your CMakeLists.txt for emscripten. For the linker to execute successfully it will need the GLFW symbols which cannot be built by you. Luckily emscripten provides those symbols when you add the "-s USE_GLFW=3" to your linker. To do so you can add these lines somewhere in the root of your CMakeLists.txt
```cmake
if (EMSCRIPTEN)
@ -107,140 +147,103 @@ if (EMSCRIPTEN)
endif ()
```
### Preparing your raylib game for web
## 3. Setup raylib game for web
To compile your game for web there are two possible scenarios:
To setup your game to compile for web there are two possible options:
Main reason to avoid the standard game `while()` loop is related to the way browsers work; the browser needs to control the executed process and just allow a single Update-Draw execution in a time-frame, so execution could be controlled and locked when required (i.e. when the tab is not active or browser is minimized). More details [here](https://emscripten.org/docs/porting/emscripten-runtime-environment.html#browser-main-loop)
To avoid the loop, code must be slightly adapted. Basically it implies moving all your `Update` and `Draw` code to an external function, possibly called `UpdateDrawFrame()`, and consequently manage all required variables from a global context.
To avoid the loop, code must be **slightly adapted**. Basically it implies moving all your `Update` and `Draw` code to an external function, possibly called `UpdateDrawFrame()`, and consequently manage all required variables from a global context.
For a simple example on code refactoring for web, check [core_basic_window_web.c](https://github.com/raysan5/raylib/blob/master/examples/core/core_basic_window_web.c) example. For more complex examples, just check `raylib/templates` directory, all templates are ready to work on any platform with no changes required.
For a simple example on code refactoring for web, check [`core_basic_window_web.c`](https://github.com/raysan5/raylib/blob/master/examples/core/core_basic_window_web.c) example. For a more complex example, just check [`raylib-game-template`](https://github.com/raysan5/raylib-game-template), game template includes an already configured `Makefile` ready to compile it for web.
Avoiding `while()` loop will give better control of the program to the browser and it will run at full speed in the web.
2. Using standard raylib `while(!WindowShouldClose())` loop
There could be some situations where the game `while()` loop could not be avoided and users need it to deal with it. For those situations, emscripten implemented two solutions: [`EMTERPRETER`](https://emscripten.org/docs/porting/emterpreter.html#emterpreter-async-run-synchronous-code) and the new [`ASYNCIFY`](https://emscripten.org/docs/porting/emterpreter.html#comparison-to-asyncify). Both methods basically detect synchronous code and move those pieces of code to an asynchronous mode at compilation.
### 3.2 Use standard raylib `while(!WindowShouldClose())` loop
raylib examples [`Makefile`](https://github.com/raysan5/raylib/blob/master/examples/Makefile) have been adapted to use [`ASYNCIFY`](https://github.com/raysan5/raylib/blob/master/examples/Makefile#L236), they work great (because they are very small samples), but note that there is a performance penalization.
There could be some situations where the game `while()` loop could not be avoided and users need to deal with it. For those situations, emscripten implemented [`ASYNCIFY`](https://emscripten.org/docs/porting/asyncify.html). `ASYNCIFY` basically detect synchronous code and allows it to run asynchronous. Enabling ASYNCIFY just requires an additional compilation flag passed to `emcc` when compiling game code.
### Compiling raylib game
raylib examples [`Makefile`](https://github.com/raysan5/raylib/blob/master/examples/Makefile) has been adapted to use [`ASYNCIFY`](https://github.com/raysan5/raylib/blob/master/examples/Makefile#L310) by default, they work great but note that there is a small performance penalization.
To compile your raylib code for web, it's recommended you use an already setup `Makefile` ready for that, [raysan5/raylib-game-template](https://github.com/raysan5/raylib-game-template) could be used for reference.
## 4. Compile raylib game for web
Before compiling the game, review the copied `Makefile` to make sure emscripten sdk path (`EMSDK_PATH`) and other paths are correctly set. Also review the following `Makefile` variables: `PROJECT_NAME`, `RAYLIB_PATH`, `PROJECT_SOURCE_FILES`.
### 4.1 Commmand-line compilation
Once `Makefile` has been reviewed, to compile raylib source code, just execute the following `make` call from command line:
make PLATFORM=PLATFORM_WEB -B
_If you get "emcc: command not found" or a similar error when running `make` but the paths are correct, add `-e` to the end of the make command._
_Note that required resources should be embedded into a `.data` file using the compiler parameter `--preload-file filename.ext` or `--preload-file folder` (already configured in the `Makefile` to use `resources` directory)._
Compilation will generate several output files:
To compile raylib game directly from the command line, those are the commands to run:
* Without `ASINCIFY`
```
project_name.html > HTML5 game shell to execute the game
project_name.js > Glue code to load WebAssembly program
To test the newly created `.html` file (and its .wasm, .js and .data), just create a `localhost` (you can do it using python) and open the web in the browser.
Create `localhost` using python, make sure you set the local host to the same folder where your `.html` file is located or keep in mind that the directory from which you set the localhost is the base directory for browser access.
**Using Python 2.7.x**, create a localhost by creating the following script:
```python
# wasm-server.py
import SimpleHTTPServer
import SocketServer
class WasmHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def end_headers(self):
# Include additional response headers here. CORS for example:
The compilation line is quite standard, similar to any other compiler and platform. Here a small explanation of the different parameters:
```
-o game.html // Output file, the .html extension determines the files that need to be generated: `.wasm`, `.js` (glue code) and `.html` (optional: `.data`). All files are already configured to just work.
game.c // The input files for compilation, in this case just one but it could be multiple code files: `game.c screen_logo.c screen_title.c screen_gameplay.c`
-Os -Wall // Some config parameters for the compiler, optimize code for small size and show all warnings generated
./path-to/libraylib.a // This is the libraylib.a generated, it's recommended to provide it directly, with the path to it: i.e. `./raylib/src/libraylib.a`
-Ipath // Include path to look for additional #include .h files (if required)
-Lpath // Library path to look for additional library .a files (if required)
-s USE_GLFW=3 // We tell the linker that the game/library uses GLFW3 library internally, it must be linked automatically (emscripten provides the implementation)
-s ASINCIFY // Add this flag ONLY in case we are using ASYNCIFY code
--shell-file path-to/shell.html // All webs need a "shell" structure to load and run the game, by default emscripten has a `shell.html` but we can provide or own
```
... and executing
python wasm-server.py
There are some additional emscripten flags that can be useful if the game requires them. For example, in case of resources loading (images, textures, audio, fonts, models..), they need to be compiled with code (`.data` file generated). Web games use an internal Virtual-File-System to store data. Also note that the maximum memory size required by the application (considering everything the game will load) SHOULD be provided.
**Using Python 3.x**, create a localhost using:
Here some of those additional flags:
```python
# Python 3
```
--preload-file resources // Specify a resources directory for data compilation (it will generate a .data file)
-s TOTAL_MEMORY=67108864 // Specify a heap memory size in bytes (default = 16MB) (67108864 = 64MB)
To configure all required compilation flags for web, an already setup `Makefile` is provided, you can check [raysan5/raylib-game-template](https://github.com/raysan5/raylib-game-template) for reference.
Before compiling the game, review the `Makefile` to make sure emscripten sdk path (`EMSDK_PATH`) and other paths are correctly setup. Also review the following `Makefile` variables: `PROJECT_NAME`, `RAYLIB_PATH`, `PROJECT_SOURCE_FILES`.
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()
```
Once `Makefile` has been reviewed, to compile raylib source code, just execute the following `make` call from command line:
... and executing
make PLATFORM=PLATFORM_WEB -B
python3 wasm-server.py
_If you get "emcc: command not found" or a similar error when running `make` but the paths are correct, add `-e` to the end of the make command._
Finally, access your game in the browser using:
_Note that required resources should be embedded into a `.data` file using the compiler parameter `--preload-file filename.ext` or `--preload-file folder` (already configured in the `Makefile` to use `resources` directory)._
localhost:8080/project_name.html
Compilation will generate several output files:
Alternatively, if you have the emscripten binaries in your path, you can run the following command
```
emrun project_name.html
project_name.html > HTML5 game shell to execute the game
project_name.js > Glue code to load WebAssembly program
project_name.wasm > WebAssembly program
project_name.data > Required resources packaged
```
And this will be the equivalent to writing the `wasm-server.py` program.
### Building with CLion/CMake/Emscripten for Web
### 4.3 Using CMake
Use the following **CMake options** (find them under **CLion > Preferences > Build,Execution,Deployment > CMake**)
Do not use the **-S, -H, -B** options mentioned in the wiki because they get in the way. **-B build** in particular was preventing **CLion** from finding the generated files (because it was creating an additional build directory inside CLion's own directory).
Included this snippet at the top of `CMakeLists.txt`, with no changes whatsoever:
```
@ -250,4 +253,34 @@ if (EMSCRIPTEN)
endif ()
```
_This was tested under both **macOS Catalina 10.15.7** and **Windows 10** with **CLion 2020.2** and **Emscripten 2.0.13**_
## 5. Test raylib game on web
To test the newly created `.html` file (and its `.wasm`, `.js`, `.data` and maybe `.mem`), you can create a `localhost` (you can do it using python) and open the web in the browser.
To create a `localhost` using python, from command-line go to the same folder where your `.html` file is located or keep in mind that the directory from which you set the localhost is the base directory for browser access. Execute the following command-line (it requires Python 3, provided by emscripten):
```
python -m http.server 8080
```
It will allow you to access the webpage from a browser directly from that directory with the web address:
```
localhost:8080/project_name.html
```
Alternatively, if you have the emscripten binaries in your path, you can run the following command
```
emrun project_name.html
```
## 6. Upload raylib web game to itch.io
To upload a raylib web game to itch.io you need to rename `project_name.html` to `index.html` and compress into `game.zip` the files generated on compilation:
```
index.html
game.wasm
game.js
game.data
game.mem
```
Upload the `game.zip` to a new itch.io project and select the option for the file: `This file will be played in the browser`.
You have some additional config options on the itch.io game section: `Embed options`
When saving the page and entering your itch.io game page, it should be playable!