My personal, minimal game template for creating cross-platform raylib games in Python, powered by pocketpy.
- Includes raylib (v6.0) and pocketpy (v2.1.8), built from source and version-pinned
- Write your whole game in Python, with live hot reload while developing
- Cross-platform builds (Linux-x64, Windows-x64 & Web)
- Autogenerated Python bindings and type stubs
- Includes build helper script for easy development
- Automatic asset packing/loading using a zip pak + virtual filesystem
- Persistent
save/directory on all platforms, IndexedDB backed on web
Note
The previous LuaJIT (and C/C++) version lives on the luajit branch.
git,zip, andzig0.16pkg-config, OpenGL, X11 and Wayland development libraries- Optional:
emscriptenfor the web target - Optional:
curlandpython3withpycparserandpcpp, only needed for./build.sh bindgen - Develop on Linux, or on Windows via WSL2
Tip
See the wiki for quick installation commands
Clone the repository:
git clone --depth 1 https://github.com/ingur/raylib-starter.gitUse the helper script to build and run the game:
./build.sh runThere is also a hot reload mode. Save a file and the game reloads:
./build.sh devYou can use the following commands:
# usage: ./build.sh <command> [options]
./build.sh run # build and run the game [debug|release]
./build.sh dev # build and run the game with hot reload
./build.sh linux # build the linux target [debug|release]
./build.sh windows # build the windows target [debug|release]
./build.sh web # build the web target
./build.sh dist # package release zips for all platforms into dist/
./build.sh bindgen # regenerate the python bindings and type stubs
./build.sh clean # clean build environment
./build.sh help # show the help messageBuild targets default to release, run defaults to debug.
Note
The first build downloads and compiles raylib and pocketpy from source. Later builds are incremental. Binaries land in zig-out/.
import raylib as rl
# top level runs once at boot, the window is already open.
# define update(), it runs every frame (return True to quit)
def update():
rl.BeginDrawing()
rl.ClearBackground(rl.RAYWHITE)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY)
rl.EndDrawing()- The Python entrypoint is
game/main.py - Define
update()to run code every frame - Return
Truefromupdate()to quit the game - Window startup settings live in
game/window.py - Debug builds enable the
DEVbuiltin ./build.sh devenables hot reloading./build.sh run releaseplays the packed build, exactly what players get- The windows target cross-compiles from Linux, no extra toolchain needed
- Define
before_reload()andafter_reload(state)to carry a state string across reloads - Reloads never free GPU resources
- Unload them in
before_reload()like the demo does - Project name lives in
build.zig, dependency versions inbuild.zig.zon - Rerun
bindgenafter a raylib or pocketpy bump - Vectors and colors come from the built-in
vmathmodule (vec2,vec3,color32) assets/andgame/are packed intoassets.pak, a plain zip- Files load through a virtual filesystem
- Loose files win over the pak:
sprite = rl.LoadTexture("assets/sprite.png")
- Shaders are assets too and load the same way
- Desktop uses GLSL 330 and web uses GLSL ES 100
- Ship both and pick at runtime:
import sys version = "glsl100" if sys.platform == "emscripten" else "glsl330" shader = rl.LoadShader("", f"assets/shaders/{version}/scanline.fs") # "" = default vertex shader
- Write save files to
save/, which exists and persists on every platform:rl.SaveFileText("save/highscore.txt", str(score))
- On web, anything written outside
save/is in-memory only - Read any packed or loose file with
rl.LoadFileTextandrl.LoadFileData, which returnstrandbytes - The builtin
open()only sees real files likesave/ - Use the
rlloaders for packed assets - Some raylib functions take C pointers, shown as
intptrin the stubs - The built-in
stdcmodule bridges them withaddressof,malloc, and typed boxes likeFloat - Test web builds locally with
emrun zig-out/web/index.html
