Cached at:
08/20/26, 12:43 PM
# b12n-raylib-jlt — raylib examples in Jolt
Source: [https://raylib-jlt.b12n.app/](https://raylib-jlt.b12n.app/)
75**raylib**examples written in**Jolt**— native Clojure on Chez Scheme, no JVM\. They call the real`libraylib`directly over its C ABI through`jolt\.ffi`: no wrapper library, no codegen, no C shim\.
## 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 Small*float*structs go in floating\-point registers, which the pointer trick does not cover\. So geometry that takes a`Vector2`or`Vector3`by value is drawn with rlgl's scalar immediate mode instead —`rlVertex3f`,`rlColor4ub`, and the matrix stack for nested transforms\.
## How it fits together
```
flowchart LR
subgraph ex["75 example namespaces"]
e["pong · boids · tetriscamera-3d · penrose-tiling · …"]
end
subgraph shared["net.b12n.raylib-jlt.raylib — one shared layer"]
kw["keyword-argument drawing APItext! · rect! · circle! · cube!"]
fb["jolt.ffi/defcfn bindspositional, mirroring C"]
kw --> fb
end
lib["system libraylibdlopened 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 / apk
bb 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`\.[babashka](https://babashka.org/)is 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\.