raylib examples in Jolt
A few of them
bb tetris
bb boids
bb rlgl-solar-system
bb fourier-epicycles
bb double-pendulum
bb penrose-tiling
bb spinning-cubes
bb asteroids
games 10 · core 9 · shapes 32 · text 5 · 3d 12 · generative 7 — every one of them, full size.
Why this is interesting
raylib passes structs by value everywhere, and Chez's `foreign-procedure` cannot. Nothing here works around that with a C shim — instead each struct gets the treatment its size and contents actually earn on the ABI. Three facts drive every distinctive decision in the binding layer:
- #### Color → a packed :uint `Color` is 4 bytes of `u8`, and a 4-byte all-integer struct travels in one general-purpose register — exactly like a `uint32`. So every draw call passes color as an int, and no struct marshaling happens at all.
- #### Camera2D / Camera3D → by pointer A composite larger than 16 bytes is passed indirectly on AArch64 — the caller allocates a copy and passes its address. So the 24-byte `Camera2D` and 44-byte `Camera3D` are built in native memory and bound as `[:pointer]`. This one is AArch64-specific, and the guide says so plainly rather than pretending it ports.
- #### Vector2 / Vector3 → rlgl immediate mode
How it fits together
flowchart LR subgraph ex["75 example namespaces"] e["pong · boids · tetris camera-3d · penrose-tiling · …"] end subgraph shared["net.b12n.raylib-jlt.raylib — one shared layer"] kw["keyword-argument drawing API text! · rect! · circle! · cube!"] fb["jolt.ffi/defcfn binds positional, mirroring C"] kw --> fb end lib["system libraylib dlopened at runtime, C ABI"] e --> kw fb -->|"Color as a packed :uint"| lib fb -->|"Camera2D/3D by pointer"| lib fb -->|"rlgl scalar immediate mode"| lib Every example is a small namespace over that one shared layer. Adding one touches exactly four places — the source namespace, a `deps.edn` alias, a `check.clj` require, and a `bb.edn` registry row.
What's here
#### One binding layer
All the FFI binds, the keyword-argument drawing API, and the named color palette live in a single namespace. Examples stay small enough to read in one sitting.
#### Reads like Clojure, not like C
`(rl/circle! :x 400 :y 225 :radius 50 :color rl/MAROON)` — positional binds at the boundary, keyword wrappers on top.
#### Provable without a display
`RAYLIB_APP_AUTO_QUIT_MS` closes the window on a timer and `RAYLIB_APP_SHOT` dumps a frame to PNG — so a windowed example can prove itself unattended.
#### A compile gate over all 75
`bb check` requires every example namespace headlessly. No window, no JVM, and it catches a broken binding across the whole suite at once.
#### clj-kondo taught the FFI
A hook rewrites each `defcfn` into an equivalent `defn`, so call sites get arity- and return-type-checked instead of producing \~500 false positives.
#### Not only raylib
The two clock examples call plain libc `time()` / `localtime()`, reading `struct tm` straight out of native memory. Nothing about `jolt.ffi` is raylib-specific.
Quickstart
bb lib:check # is the native libraylib installed for this OS/arch?
bb lib:install # …install it via brew / pacman / apt / dnf / zypper / apkbb info # grouped cheat-sheet of every example
bb tetris # run one (opens a window)
bb run-all 10 # demo reel: every example, 10s each
bb check # headless compile-check of all 75, no window
Needs `jolt` (tested against v0.7.16) and a system `libraylib`. babashkais optional but gives every example a friendly task; without it each one is a `joltc -M:<alias>` away. Released under the zlib/libpng license — the same one raylib itself uses.