-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Working for Web (HTML5)
raylib C code can be compiled to 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.
emscripten
provides a set of tools to compile C code to WebAssembly, the main tool provided is the emcc
compiler. emcc
is actually a direct replacement for gcc
, so, anyone with experience compiling code directly in the command-line should not have much trouble to use emcc
.
There are some additional compilation flags for emcc
compilation and code linkage, so, a Makefile
is provided in raylib/src/Makefile
to simplify the compilation process, it only requires defining PLATFORM_WEB
to use the correct compilation flags.
The complete process to compile for web is detailed below. The main steps to follow are:
- Install emscripten toolchain
- Compile raylib library
- Build examples for the web
- Setup raylib game for web
- Compile raylib game for web
- Test raylib game on web
- Upload raylib web game to itch.io
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.
Download emscripten SDK from GitHub, download as a zip and decompress it in C:\emsdk
folder. Those pages also provide detailed instructions.
emsdk
requires Python and Git 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, on Linux proceed to next step).
Execute the following commands to install and activate latest emscripten tools:
emsdk update
emsdk install latest
emsdk activate latest
On Linux and macOS you will also need to set the proper environment so that raylib build system can find the Emscripten compiler:
source ./emsdk_env.sh
NOTE: Updated installation notes are always available here.
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.
To compile raylib library directly from the command line, those are the commands to run:
emcc -c rcore.c -Os -Wall -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2
emcc -c rshapes.c -Os -Wall -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2
emcc -c rtextures.c -Os -Wall -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2
emcc -c rtext.c -Os -Wall -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2
emcc -c rmodels.c -Os -Wall -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2
emcc -c utils.c -Os -Wall -DPLATFORM_WEB
emcc -c raudio.c -Os -Wall -DPLATFORM_WEB
emar rcs libraylib.a rcore.o rshapes.o rtextures.o rtext.o rmodels.o utils.o raudio.o
Alternatively to -DGRAPHICS_API_OPENGL_ES2
, the option -DGRAPHICS_API_OPENGL_ES3
can also be passed.
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.
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.
From command-line, the following line must be called:
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 the web platform is set (SET PLATFORM=PLATFORM_WEB
) and click OK to run the script.
Note that current raylib/src/Makefile just adds/replace the compiled modules to any existing libraylib.a
(instead of recreating it) so, if you had a previously compiled libraylib.a
for desktop and you recompile raylib src for web, old modules are still inside libraylib.a
. Solution: just delete libraylib.a
and recompile it for PLATFORM_WEB
or the new platform.
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.
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.
You must set the (PATH
) to :
$(shell printenv PATH):$(EMSDK_PATH):$(EMSCRIPTEN_PATH):$(CLANG_PATH):$(NODE_PATH):$(PYTHON_PATH)
As example, your MakeFile
on Linux should look similar to:
EMSDK_PATH ?= /path/to/emsdk
EMSCRIPTEN_PATH ?= $(EMSDK_PATH)/upstream/emscripten
CLANG_PATH = $(EMSDK_PATH)/upstream/bin
PYTHON_PATH = /path/to/python
NODE_PATH = $(EMSDK_PATH)/node/12.9.1_64bit/bin
PATH = $(shell printenv PATH):$(EMSDK_PATH):$(EMSCRIPTEN_PATH):$(CLANG_PATH):$(NODE_PATH):$(PYTHON_PATH)
After the path configuration, just execute the following command:
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.
Generated libraylib.a
is placed in raylib\src\libraylib.a
directory.
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
You can go with the emscripten suggested way: That is to use the following command that will add the compiler and toolchain options for you:
emcmake cmake -H . -B build
(The "-H ." is deprecated since CMake 3.13 and if you are using higher version please replace the argument with "-S .")
Emscripten cmake will prefer the ninja build system generator if you have that installed (and you should).
IDE-friendly option: As the first option is only available from a command line where you have run "emsdk activate latest" when using an IDE (like Visual Studio, CLion, etc.) you should set the path to the emscripten toolchain file:
cmake -H . -B build -G Ninja -DPLATFORM=Web "-DCMAKE_TOOLCHAIN_FILE=<fullpath_to_emsdk>/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
(The "-H ." is deprecated since CMake 3.13 and if you are using higher version please replace the argument with "-S .") (The ninja generator is optional and you can use your system default by removing "-G Ninja".)
One note here - if you're using vcpkg for package management and have installed raylib:wasm32-emscripten you should execute this instead:
cmake -H . -B build -G Ninja "-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"
- Building with CMake
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
if (EMSCRIPTEN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s GL_ENABLE_GET_PROC_ADDRESS=1")
set(CMAKE_EXECUTABLE_SUFFIX ".html") # This line is used to set your executable to build with the emscripten html template so that you can directly open it.
endif ()
At this point, if you (optionally) want to compile the provided examples for the web, there is another Makefile to configure. Make sure all paths to emscripten installation (EMSDK_PATH
) and emscripten required tools (Clang, Python, Node) are correctly configured on raylib/examples/Makefile
, you must verify these lines.
Start up Notepad++ for raylib, open the raylib/examples/Makefile
file, press F6, choose raylib_makefile
, verify that in the script the web platform is set (SET PLATFORM=PLATFORM_WEB
) and click OK to run the script. It will compile all the examples for the web.
Opening generated .html
files directly from disk may fail due to web browser security configuration. You should follow steps on Test raylib game on web.
To setup your own 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
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
example. For a more complex example, just check 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.
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
. 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.
raylib examples Makefile
has been adapted to use ASYNCIFY
by default, they work great but note that there is a small performance penalization.
To compile raylib game directly from the command line, those are the commands to run:
NOTE: in
/raylib/src/
there is a file calledminshell.html
which is the recommended shell file for Raylib applications. So you may pass--shell-file $HOME/raylib/src/minshell.html
toemcc
if you had Raylib in your home directory.
- Without
ASYNCIFY
emcc -o game.html game.c -Os -Wall ./path-to/libraylib.a -I. -Ipath-to-raylib-h -L. -Lpath-to-libraylib-a -s USE_GLFW=3 --shell-file path-to/shell.html -DPLATFORM_WEB
- With
ASYNCIFY
emcc -o game.html game.c -Os -Wall ./path-to/libraylib.a -I. -Ipath-to-raylib-h -L. -Lpath-to-libraylib-a -s USE_GLFW=3 -s ASYNCIFY --shell-file path-to/shell.html -DPLATFORM_WEB
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 ASYNCIFY // 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 our own
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.
Here are some of those additional flags:
--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)
-s ALLOW_MEMORY_GROWTH=1 // Allow automatic heap memory resizing -> NOT RECOMMENDED!
-s FORCE_FILESYSTEM=1 // Force filesystem creation to load/save files data (for example if you need to support save-game or drag&drop files)
-s ASSERTIONS=1 // Enable runtime checks for common memory allocation errors (-O1 and above turn it off)
--profiling // Include information for code profiling
To configure all required compilation flags for web, an already setup Makefile
is provided, you can check 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
.
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:
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
Use the following CMake options:
-DCMAKE_TOOLCHAIN_FILE=<YOUR PATH HERE>/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
-DPLATFORM=Web
Included this snippet at the top of CMakeLists.txt
, with no changes whatsoever:
if (EMSCRIPTEN)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s GL_ENABLE_GET_PROC_ADDRESS=1")
set(CMAKE_EXECUTABLE_SUFFIX ".html") # This line is used to set your executable to build with the emscripten html template so taht you can directly open it.
endif ()
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
To upload a raylib web game to itch.io you need to rename project_name.html
to index.html
and compress into project_name.zip
the files generated on compilation:
index.html
project_name.wasm
project_name.js
project_name.data
project_name.mem
Upload the project_name.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!
Q: When compiling my game I got this error with libraylib.a
:
wasm-ld: error: unknown file type: rglfw.o
A: That's because it includes symbols from a previous compilation. Just delete all generated .o and libraylib.a
and compile it again for web.
Q: Mouse Input not being detected?
A: Be sure that the Input Detection is in front of the Frame Draw. For some reason input isn't detected after the frame has been drawn.
Q: Failing to load resource files? (WARNING: FILEIO: [...] Failed to open file
)
A: Make sure that you refer to the resource directory in the same way in the code and in the --preload-file
compiler argument. If you passed an absolute path to the compiler (e.g. C:\my_game\resources
) you'll have to use absolute paths in the code (e.g. "C:/my_game/resources/player.png"
), and if you passed a relative path to the compiler (e.g. ..\resources
) you'll have to use relative paths in the code (e.g. "resources/player.png"
).
Q: Failing to play sounds? (Uncaught ReferenceError: ccall is not defined at device.scriptNode.onaudioprocess
)
A: Add this argument in the compiler line -s EXPORTED_RUNTIME_METHODS=ccall
.
Q: Why don't I see the changes I compiled being applied?
A: Your web browser may be caching the game; you need to clear the cache. In most browsers the shortcut Shift+F5 or Ctrl+F5 will fully reload the page.
Please, feel free to add here your FAQ/Issues to help others!!!
www.raylib.com | itch.io | GitHub | Discord | YouTube
- Architecture
- Syntax analysis
- Data structures
- Enumerated types
- External dependencies
- GLFW dependency
- libc dependency
- Platforms and graphics
- Input system
- Default shader
- Custom shaders
- Coding conventions
- Integration with other libs
- Working on Windows
- Working on macOS
- Working on GNU Linux
- Working on Chrome OS
- Working on FreeBSD
- Working on Raspberry Pi
- Working for Android
- Working for Web (HTML5)
- Creating Discord Activities
- Working anywhere with CMake
- CMake Build Options
- raylib templates: Get started easily
- How To: Quick C/C++ Setup in Visual Studio 2022, GCC or MinGW
- How To: C# Visual Studio Setup
- How To: VSCode
- How To: Eclipse
- How To: Sublime Text
- How To: Code::Blocks