Screen.Recording.2025-01-22.at.03.45.21.mov
Make games using Odin + Raylib that works in browser and on desktop. Suitable for small games such as gamejam creations.
Live example: https://zylinski.se/odin-raylib-web/
- Emscripten. Download and install somewhere on your computer. Follow the instructions here: https://emscripten.org/docs/getting_started/downloads.html (follow the stuff under "Installation instructions using the emsdk (recommended)").
- Recent Odin compiler: This uses Raylib binding changes that were done on January 1, 2025.
- Change
EMSCRIPTEN_SDK_DIR
inbuild_web.bat/sh
to point to your emscripten setup. - Run
build_web.bat/sh
. - Web game is in the
build/web
folder.
Note
build_web.bat
is for windows, build_web.sh
is for Linux / macOS.
Warning
You may not be able to start build/web/index.html
directly, because you'll get "CORS policy" javascript errors. You can get around that by starting a local web server using python. Go into build/web
and run:
python -m http.server
Open localhost:8000
in your browser to play the game.
If you don't have python, then emscripten actually comes with it. Look in the python
folder of where you installed emscripten.
You can also build a desktop executable using build_desktop.bat/sh
. It will end up in the build/desktop
folder.
Put any assets (textures, sounds etc) you want into the assets
folder. It will be merged into the web build when the emscripten compiler runs. It is also copied to the build/desktop
folder when you make a desktop build.
- raylib, raygui, rlgl using the default
vendor:raylib
bindings. - Allocator that works with maps and SIMD.
- Temp allocator.
- Logger.
- There's a wrapper for
read_entire_file
andwrite_entire_file
fromcore:os
that works on web as well. Seesource/os
package. It's used insource/game.odin
to load a file. - You can load any file in the
assets
folder.
Note
The files written using write_entire_file
don't really exist outside the browser. They don't survive closing the tab. But you can write a file and load it within the same session. You can use it to make your old desktop code run, even though it won't be possible to really save anything.
- Anything from
core:os
that isn't in thesource/os
package. fmt.print
and similar procs. Instead, uselog.info
andlog.infof
. Note:fmt.tprintf
(temp string formatting) still works!
I recommend debugging the desktop build when you can (add -debug
inside build_desktop.bat/sh
and use for example RAD Debugger). But if you get web-only bugs then you can add -g
to the the emcc
line in the build script. This will give you crash stack traces with useful information. It works in Chrome, but I didn't get it to work in Firefox.
There is a Sublime project file: project.sublime-project
. It has a build system pre-setup that lets you run the build scripts for both web and desktop.
The contents of the main_web
folder is built in freestanding_wasm32
build mode. That package also imports the game
package. So it's the whole game. freestanding
means that no OS-specific stuff at all is included. wasm32
means that the output is possible to run in a web browser.
Odin supports compiling to a js_wasm32
target that has less limitations. However, we cannot use that because raylib
requires emscripten in order to translate its OpenGL calls into WebGL. Emscripten has some hacks to pull in its own C standard library stuff, so that's sort-of the "OS layer" you have in emscripten: Strange libc-in-a-web-browser. The Odin core libs don't support emscripten and never will. So that's why we use freestanding
.
When main_web
has been compiled into an object file called game.wasm.o
, then the emscripten compiler emcc
is run. It is fed both the game.wasm.o
file and also compiles the main_web/main_web.c
file. That C file says what will happen when our game is run in a web browser: It'll call our Odin code! (we also feed emcc
the prebuilt raylib and raygui wasm libs).
Since our odin code is compiled using freestanding
, no allocators or anything is set up. That's why source/main_web/main_web_entry.odin
sets up an allocator, temp allocator and logger in the web_init
proc.
The allocator uses the libc procedures malloc
, calloc
, free
and realloc
that emscripten exposes.
There's also a logger that uses the puts
procedure that emscripten exposes, in order to print to the web browser console.
Like I said, we can't use core:os
at all. Therefore I've made a tiny wrapper in source/os
that implements read_entire_file
and write_entire_file
that both work in web and desktop mode. The web mode once again uses emscripten things to read from the data that is baked into the built web app (the stuff in the assets
folder). The desktop mode just runs the normal core:os
code.
I have updated my Odin + Raylib + Hot Reload template with similar capabilities: https://github.com/karl-zylinski/odin-raylib-hot-reload-game-template -- Note that it's just for making a release web build, no hot reloading is supported with the web version!
Ask questions in my gamedev Discord: https://discord.gg/4FsHgtBmFK
Caedo's repository and Aronicu's repository helped me with:
- The initial emscripten setup
- The logger setup
- The idea of using python to host a server