b12n-raylib-jnk — Guide
User-facing documentation for b12n-raylib-jnk: 209 raylib examples ported to jank — a native Clojure dialect (C++/LLVM), not the JVM. Each page below covers one interop pattern or raylib API surface, citing the example file that proves it.
Why this exists
jank is young, and almost nothing has been written about using it against a real C library at this scale. Porting 209 raylib examples surfaced a set of interop rules that are not obvious from jank's own documentation and that cost real debugging time to find. Each page here is one of those rules, written up with the committed example that proves it — so the next person does not have to rediscover it by bisecting a failing draw loop.
What b12n-raylib-jnk is
209 of the official raylib examples — shapes, core, text, textures, shaders, models, and audio — each a small jank namespace under raylib-examples/src/raylib_examples/, sharing one C-binding wrapper, jank-raylib-sys, that exposes raylib's C API directly.
It is the native-Clojure sibling of b12n-raylib-jlt (raylib in Jolt/Chez Scheme) and of an unreleased JVM-Clojure port over coffi/Panama. All three bind the same C library directly; what differs is the boundary each language draws between its own values and C's:
jolt and JVM Clojure cross the FFI boundary at the call: an FFI call marshals values in and out, but a raylib struct can otherwise live anywhere a normal value lives. jank draws the boundary at the value itself: a native C++ value (
Color,Vector2,Model, ...) can be constructed and used inline, but it cannot cross a jank function boundary — not returned, not passed as a parameter, not carried throughloop/recur. Every pattern in this guide is a consequence of that one rule.
Four things follow from it:
- The value can't leave the form that produced it — construct inline, bind in an enclosing
let, or park frame-crossing mutable state in acpp/rawstatic. (native-value-lifetimes.md) - The compiler enforces it at compile time, strictly —
if/condbranch type-checking, numeric coercion between jank and native number types, and struct construction all have sharp, well-defined rules. (type-checking-and-coercion.md) - A C-interop toolbox reaches everything the rule seems to block — pointer interop (
cpp/&,cpp/aget,cpp/new), out-params, callbacks defined insidecpp/raw, and shared C headers shipped by a wrapper. (cpp-interop-toolbox.md) - The full raylib surface is reachable despite the rule — fonts, models and animations, audio, 3D mode, rlgl, and (platform-permitting) compute shaders all work, proven example by example. (
raylib-api-coverage.md)
Nothing about jank's C++ interop is raylib-specific — (:include "header.h") and cpp/ reach any C/C++ library. This repo just happens to exercise it against one real, struct-heavy graphics API across 209 examples.
Capability pages
The interop core (the reason this repo is interesting)
native-value-lifetimes.md— the one rule that explains most crashes, frame-crossing mutable state viacpp/rawstatics (and the per-fn-static duplication gotcha), and create-once resources via outer-letcapture.type-checking-and-coercion.md—if/condbranch type-checking (including theand/orgotcha), the numeric-traps table (mod/quot/cpp/float/min/max), and constructing native structs from jank data.cpp-interop-toolbox.md— pointer interop (cpp/&,cpp/aget,cpp/new,cpp/raw),int *out-params, callback-taking APIs, shared C headers shipped by a wrapper (jank_rlights.h), shader-uniform shims, and current limitations (known-blocked constructs).
What's proven to work
raylib-api-coverage.md— fonts, models and animations, audio, 3D mode, rlgl + textures, and compute shaders (with the platform caveat — see the root README's Known limitations).jvm-surface-gaps.md— what replaces the missing JVM surface (Math/*,format, char literals), what's actually available (clojure.core/clojure.string, with caveats), and a few gotchas that save a recompile.
Orientation
getting-started.md— requirements, cloning with the submodule, and thebbtask surface.porting-workflow.md— the end-to-end process for porting one example: source of truth, file layout, the five-place registration, the headless smoke test.example-catalog.md— a tour of all 209 examples grouped by raylib category, and how to add one (now with a preview GIF per recorded example — seedocs/demos/README.mdfor the full gallery).raygui-to-keyboard.md— the pattern for porting raygui-based examples (sliders/checkboxes) to keyboard controls.
See also
b12n-raylib-jlt— the same idea in Jolt (Chez Scheme) overjolt.ffi. Its FFI boundary is per-call, not per-value — aCamera3Dcan live in an ordinary variable between FFI calls, unlike jank's native values.- An unreleased JVM-Clojure port over
coffi/Panama takes the same per-call boundary as Jolt, plus a garbage collector jank doesn't have to work around.