Senior Principal Engineer at NewsCorp Australia: ClojureDart, AI, full-stack.
Two decades in software, and I have moved with the landscape: Java → Ruby → Clojure → ClojureScript → ClojureDart for mobile and desktop → and most recently Jolt, native Clojure on Chez Scheme. Over the past year a tiny team and I have shipped a fleet of production ClojureDart apps for iOS, Android and macOS, alongside Clojure backends and ClojureScript SPAs that share a single mental model end to end. Outside work, most of my open-source time goes into Jolt: bindings, ports, a Java runtime, and fixes that travel upstream as far as Chez Scheme itself.
My current interest is where functional programming meets AI-assisted development: building maintainable agentic workflows in Clojure and Jolt, and sharing the patterns and pitfalls a small team learns by shipping at pace. When I'm not coding I'm writing experience reports for developers who appreciate the art of elegant, productive engineering.
🗣️ Next up: AI Engineer Sydney 2026 on December 7, fresh off Clojure/Conj 2026. I'm generally available to talk about ClojureDart, MCP/agent tooling, and using LLMs in real engineering workflows. Get in touch.
At a glance
- ☕ Java inside Jolt. champaca runs real Java libraries with no JVM in the process, and Spring Boot and Quarkus apps start on it.
- 🎮 513 raylib examples in three Clojures, Jolt, jank and JVM Clojure, plus Pac-Man four ways and 137 scenes on an iPhone.
- 🔧 Upstream fixes merged into cisco/ChezScheme and atgreen/openldk, and 79 merged pull requests to Jolt.
- 🌱 jlt-commons, the community home for Jolt libraries I started in August, now at 44 public repos.
- 🇦🇺 clj-aus, Australia's Clojure meetup. Session two is online on Tuesday 24 November.
- 🔜 AI Engineer Sydney 2026 · Hilton Hotel, Sydney · Mon 7 Dec, 4:30 PM
209 ports in six days, in a language the model barely knew - 📺 Clojure/Conj 2026 · Charlotte, NC · Oct 2026
Production ClojureDart for iOS, Android, and macOS. The recording is on its way. - 📺 Clojure/Conj 2025 · Charlotte, NC · Nov 2025
From Scripts to Buy-In: How Small Clojure Wins Create Big Opportunities. ▶ Watch
The three abstracts
🔜 AI Engineer Sydney 2026 · Hilton Hotel, Sydney
209 ports in six days, in a language the model barely knew 📅 Monday, December 7 · 4:30 PM · Software Engineering track
Jolt and jank are two young Clojure implementations, one running on Chez Scheme and one compiling to native code through C++/LLVM. Between them they have almost no public code for a model to have learned from. Ask Claude for jank and it confidently writes JVM Clojure, and none of that compiles. Over six days in July I ported 209 of raylib's official C examples to jank anyway, across 194 commits. Then again in Jolt, and once more in JVM Clojure over Panama. That's 422 working examples across three runtimes, every one of them driving the same C game library.
This talk is about the harness, because the model was the same model everyone in the room already uses. Everything around it is what changed.
Those are the three raylib suites further down this page, and they kept growing after July. Between them they're at 513 examples now.
📺 Clojure/Conj 2026 · Charlotte, NC · Oct 2026
Production ClojureDart for iOS, Android, and macOS
Over the past twelve months a tiny team has used ClojureDart (Clojure compiled to Dart, running on Flutter) to build more than a dozen production apps: a document scanner with on-device OpenCV, a transit tracker with realtime GTFS, a venture-capital deck triager with on-device LLM evaluation, a notes app with on-device sentence embeddings, a marketplace, an encyclopedia, a financial charting app, and more. This experience report distills the patterns that survived the trip from prototype to production: a single-atom-plus-
:watcharchitecture that replaces Riverpod and BLoC, a curated catalog of cljd-flutter pitfalls, and a reusable scaffold for new apps.
My second year on the Conj stage, and done now. The recording is still in the publishing queue, so the link goes here once it's out.
📺 Clojure/Conj 2025 · Charlotte, NC · Nov 2025
From Scripts to Buy-In: How Small Clojure Wins Create Big Opportunities
How small, high-impact Clojure automation (AWS workflows, replacing clunky Postman processes, automating Okta SSO) builds the credibility that earns you bigger Clojure opportunities at work.
Eleven meetup talks, 2024 to 2026
| Talk | Venue | Date |
|---|---|---|
| Three native Clojures, one C game library The full hour version for clj-aus's first session: the same raylib comparison across Jolt, jank and JVM Clojure, with live demos running the whole time and 20 minutes of questions after. Run online so anyone in the country could join, and the group's opening night drew a solid turnout. |
clj-aus Online, Australia-wide |
Sep 2026 |
| Three native Clojures, one C game library Lightning talk to a room of typed-FP regulars (Haskell, OCaml, Rust), showing the same raylib examples running under three different Clojures: Jolt on Chez Scheme, jank via C++/LLVM, and JVM Clojure over Panama. bb run-all kept a real window cycling through the gallery in the background for the whole talk. The question that came up: why write it in Jolt when Chez Scheme is right there? Because Jolt reads like Clojure (vectors, lists, sets, maps), so there was no learning curve to cross first. |
FP-SYD Microsoft, North Sydney |
Aug 2026 |
| raylib, driven from Lisp Open-mic demo of raylib (a C game library) driven from Lisp, across all three suites: Jolt on Chez Scheme, jank via C++/LLVM, and JVM Clojure over Panama, plus the screen-capture and UI-automation tooling built to record their demo GIFs instead of testing by hand. All three suites are open source now; the capture tooling is still unreleased. |
02Ship Sydney Meetup Level 2, 63 Dixon St, Haymarket |
Aug 2026 |
| Three lenses, one mess: logic programming for the rest of us One RBAC authorization bug through three lenses, all in plain Clojure data: Prolog for role inheritance, Z3/SMT-LIB for conflicting constraints, a Mermaid-to-Prolog translation for reachability. Live REPL demo, closing on exposing solvers as LLM tools over MCP, where not hallucinating is the whole advantage. 📊 Slides |
FP-SYD Microsoft, North Sydney |
Jun 2026 |
| Supercharging Lisp with AI: Rapid Feedback Loops with Clojure and Claude Code Lightning talk to a room of 100+ builders. The demo ran on ClojureDart, Clojure compiled to Dart on Flutter, with a live REPL plugged straight into the running widget tree. That's the loop that lets Claude Code edit, hot-reload and assert against live UI in milliseconds instead of minutes. |
AI SYD Vercel × Relevance AI, Surry Hills |
Apr 2026 |
| The Anthropic Claude Ecosystem 66 slides end to end: ~/.claude as a personal AI config layer, Claude Code across CLI/IDE/desktop/CI, the agentic loop, MCP hosts and servers, and Agent Skills' three-level progressive disclosure. 📊 Slides · PDF |
Vibe Code Meetup Sydney |
Feb 2026 |
| Clojure for Java Developers: Building MCP Servers Without the Java Pain Cut a 350+ line Spring AI MCP server with 15+ annotations down to 180 lines with zero annotations, keeping full enterprise capability. |
Sydney JVM Community Canva HQ, Sydney |
Jul 2025 |
| Clojure for the Type-Loving Functional Programmer What Clojure offers a typed-FP audience, beyond the dynamic typing and the parens. |
FP-SYD | Jul 2025 |
| Java/Spring Boot Troubleshooting on Steroids with Clojure REPL Debugging Spring Boot from a Clojure REPL, set directly against traditional remote debugging in IntelliJ. Live coding on a sample app: debug, explore and modify at runtime with no restart and no redeploy. |
Sydney Java Meetup #22 Infomedia, Sydney |
Mar 2025 |
| Bridging Two Worlds: Leveraging Lisp-like Tools to Work with Python Scripting, extending and manipulating Python from Clojure with Basilisp and libpython-clj. |
SYDJANGO | Dec 2024 |
| Overview of Clojure for FP Live demos: libpython-clj via OpenCV, GraalVM via Babashka, AWS automation with the Cognitect AWS library, whisper.clj transcription, plus Rama and Jank. |
FP-SYD | Aug 2024 |
Interview: Clojure Corner with Flexiana. Community: I co-host Gen AI Enthusiasts, 2,000+ members online, and help run clj-aus.
More on the interview and the two communities
Clojure Corner: Interview with Burin Choomnuan · Flexiana, on Clojure, tooling, and building things in the open.
I co-host Gen AI Enthusiasts (AI Users), a 2,000+ member online generative-AI community running fortnightly sessions on prompt engineering, AI tooling and news, and practical LLM use.
I also help run clj-aus, Australia's Clojure meetup. It meets monthly and is built to be national from the start, since one Australian city is thin on its own and the whole country isn't. The first session in September opened with the raylib three-Clojures talk above. Session two is on Tuesday 24 November, online from 7pm AEDT: a tour of what Jolt can do without a JVM since September, then Clojure running on a Nintendo Wii, with four games played live. Every session from this one on carries a lightning-talk slot, and first-time speakers get priority.
I write at Clojure Civitas, where 11 articles so far are mostly browser-native ClojureScript with Scittle:
| Article | Date |
|---|---|
| Making Fun Games with Clojure + Raylib | Feb 2026 |
| Clojure Conj 2025: Two Lies and a Truth Trivia | Nov 2025 |
| Browser-Native PDF Viewer with ClojureScript & Scittle | Nov 2025 |
Eight more articles
Plus b12n.net ("Lisp's Revenge"), my own blog, built with Clojure/Cryogen, where I keep browser-native slide decks: Mastering Claude Code · Diagrams as Text
raylib in jank |
Jolt on an iPhone |
glitter, GTK4 |
Pac-Man in Jolt |
ClojureDart in production. More than a dozen apps for iOS, Android and macOS in the past year: on-device OpenCV document scanning, realtime GTFS transit tracking, on-device LLM evaluation and sentence embeddings, charting, marketplaces. Most deployed internally, several bound for the app stores. That's the Conj 2026 talk.
Clojure meets AI. Agentic workflows, MCP servers, and LLM-driven developer tooling are where most of my time goes now. The first piece of that is out: mcp-tkx builds Model Context Protocol clients and servers in Clojure and ClojureScript, across five protocol revisions, and one session can serve both the handshake era and the newer stateless one at once. Plenty of the rest is still private, so those patterns tend to surface in talks and articles before the code does.
Each of the projects below opens with a click.
Java, with no JVM in the process. champaca, a Java runtime inside Jolt
champaca is the newest thing here and by some distance the biggest, at close to 600 commits since it went up in mid-October. It's a Java runtime that lives inside Jolt, rebuilt from Anthony Green's OpenLDK, a Java runtime for Common Lisp. The first time a method gets called, a translator written in Jolt's Clojure turns its bytecode into Chez Scheme source, and Chez compiles that to native code. Java objects end up as Chez records in Jolt's own heap, so calling a Java library from Jolt is an ordinary procedure call with no second runtime to talk to.
The classes are the JDK's own, read out of $JAVA_HOME/lib/modules, and the JDK boots the way
it does on a JVM, module system and all. Thirteen common libraries give the same output they
give on HotSpot, Guava, Jackson, jsoup, H2 and Byte Buddy among them, and Spring Boot 4.1.2 and
Quarkus 3.40.1 applications start. A warm Hello takes 0.39 s from exec to exit on an M1 Pro,
against 4.3 s for the first run, because a startup cache keeps whatever the earlier run
compiled. It's experimental and says so at the top of its README. A program can still reach a
JDK native nobody has written yet, JNI libraries aren't there, and a warm start still costs a
few times HotSpot's time and memory.
📖 Docs & guide ·
Limitations
It got here by way of a first attempt. jolt-openldk loads SBCL into the Jolt process as a shared library, starts it on a core with OpenLDK already warmed up, and calls across through a small C shim. That works, and it's still alpha. But it meant two Lisp runtimes sharing one process, and champaca is what happens when you take the translator idea and make Chez the only one. Both of them kept running into bugs in OpenLDK itself, and the fixes went back upstream, which the Upstream section below has more on.
Lisp against the C ABI. 513 raylib examples in Jolt, jank and JVM Clojure
Three suites of raylib demos written in three different Clojures, each one reaching the same C game library the way its own host actually allows.
Each suite is two repos now. The bindings stay where they started, and the examples moved
out into a -demo sibling where every one is its own small runnable project, pinned to the
bindings by one commit.
raylib-jlt: 187 examples in
Jolt, native Clojure on Chez Scheme. They call the
real libraylib directly over its C ABI through jolt.ffi: no wrapper library, no
codegen, no C shim. 45 shapes, 34 core, 28 textures, 28 in 3D, 20 shaders, 11 games,
10 generative, 8 text, 3 audio. That's up from 119 in September.
📖 Bindings ·
every example, in raylib-jolt-demo
bb tetris |
bb space-invaders |
bb boids |
bb waving-cubes |
bb point-cloud |
bb rlgl-solar-system |
The interesting part is the ABI. raylib passes structs by value everywhere and Chez's
foreign-procedurecannot, so each struct gets the treatment its size actually earns:Colorrides in one register as a packed:uint,Camera2D/Camera3Dgo by pointer, andVector2/Vector3geometry is drawn through rlgl's scalar immediate mode.
raylib-jnk: 212 of the 217 official
raylib examples, ported to jank: Clojure compiled to a real native
binary through C++/LLVM, plus one original. The shaders, textures, audio and text
categories are complete.
📖 Bindings guide ·
every example, in raylib-jank-demo
bb basic-pbr |
bb magnifying-glass |
bb cel-shading |
bb shadowmap-rendering |
bb spectrum-visualizer |
bb animation-blending |
Here there is no FFI at all.
(:include "raylib.h")in thensform hands the real header to the C++ compiler, and raylib becomes ordinary interop:cpp/BeginDrawing,cpp/RAYWHITE,(cpp/Camera3D (cpp/Vector3 ...) ...). The by-value structs that cost Jolt a hand-written marshalling strategy are just constructor calls. No binding DSL, no generated shim.
raylib-clj: 113 examples in JVM Clojure, over coffi and JDK 22+'s Panama Foreign Function & Memory API. 37 core, 28 shapes, 25 models, 9 games, plus audio, text, textures and shaders. 📖 Bindings · every example, in raylib-clj-demo
bb tetris |
bb asteroids |
bb background-scrolling |
bb basic-lighting |
bb solar-system |
bb lorenz-attractor |
This is the one where the runtime does the marshalling for you. A binding is a single
defcfnnaming the C symbol and its types, and Panama builds the downcall from that description, so the by-valueColorthat Jolt hand-packs into a:uintis just a{:r 255 :g 0 :b 0 :a 255}map here, serialized from adefaliaslayout. The seam shows where raylib mutates through a pointer: those calls allocate in aconfined-arena, and the arena's lifetime is the one thing the abstraction will not hide from you.
Three hosts, one C library: Chez Scheme, C++/LLVM, and the JVM. Reading the same example three ways is the clearest map I know of where each runtime puts the boundary.
The same game, four times. Pac-Man in babashka, JVM Clojure, jank and Jolt
raylib-pacman takes that comparison down to a single program. Pac-Man, written out four times over, once per runtime, with babashka joining the three above. Same maze, same ghosts keeping their 1980 personalities, same physics. Each copy is a standalone project and nothing is shared between them, which is the whole point. You can diff the four and see exactly what the runtime costs you. 📖 Docs & guide
bb babashka |
bb clj |
bb jank |
bb jolt |
The game itself barely moves between them. What changes is the drawing layer, and only because each FFI carries a different amount across the boundary. A
Coloris four bytes passed by value: coffi hands it over as a map, babashka and Jolt pack it into an integer because their FFI moves scalars only, and jank will pass one quite happily but will not let a function return one.DrawCircleSectortakes its centre by value, so two of the four cannot call it at all and build Pac-Man out of rlgl triangles instead. The trap worth knowing: raylib measures a sector from 0 = right and the Jolt wrapper from 0 = up, and getting that wrong still compiles, still runs, and simply points Pac-Man's mouth a quarter turn away from the direction he is walking.
Two more, and this time with numbers. lisp-games, timed across the same four runtimes
lisp-games runs the same experiment on two smaller programs: borkdude's helitorus, which came out of the babashka/ffi examples, and raylib's own mesh-instancing demo. Neither game is mine. Each one is a port of somebody else's work, and the credit goes in the commit that adds the code rather than into a follow-up task. 📖 Docs & guide
babashka |
clojure |
jank |
jolt |
Pac-Man compares what each FFI will carry across the boundary. helitorus compares what it costs, because it rebuilds and projects the whole surface in Clojure every frame and reports compute and draw milliseconds separately. At the default 260 rings all four hold the same frame rate, since none of them is the bottleneck yet. Push to 900 and they separate: Clojure at 2.4 ms of compute, jank at 4.3, Jolt at 5.0, babashka at 18.0. Two of those numbers were badly wrong until an ordering that looked suspicious gave them away, which is the half of benchmarking nobody writes up. mesh-instancing measures the other axis, one draw call carrying two structs by value and a block of foreign memory.
And then on a phone. raylib on a physical iPhone, 137 scenes, no JIT
raylib-ios puts raylib and SDL2 on a physical iPhone, driven from the same Jolt. Two things make it work and neither is obvious. raylib ships no iOS platform layer, so SDL2 compiled for iOS becomes one, and both link into the executable as static archives. iOS forbids generating code at run time, which rules out a JIT, so Chez's portable bytecode ships the interpreter as the app instead and nothing is compiled on the device.
It started at seventeen scenes, running at 52 to 60 fps. Three of them were byte-identical
to files written for Android, sha256 verified, because the scene contract is pure and the
platform never appears in it. The guides are about what the device taught us rather than
what the code implied: calling into C was not the bottleneck, GetFPS returns a plausible
wrong number if you call it the obvious way, and one upstream bug report got withdrawn
after hardware contradicted a correct reading of the source.
raylib-ios is just the platform now. The scenes live in raylib-ios-demo, which has grown to 137 of them, one for each desktop demo it was ported from. Every scene builds into an iPhone app of its own, and a gallery app holds all of them behind a two-level menu. 📖 Platform docs · the 137 scenes
kaleidoscope |
spirograph |
boids |
fireworks |
That Android sibling is real now too. raylib-android, ported by yogthos, runs the same scene contract as native arm64 code with no JVM anywhere in the app. I wired up its docs-engine guide. It hasn't touched real hardware yet, so there are no screenshots to show for it.
Desktop GUI, the Replicant way. glitter, a GTK4 renderer for Jolt
glitter is a
GTK4 renderer for Jolt that follows
Replicant's model rather than React's: one
application-state atom, a pure state -> hiccup view function, top-down re-render on every
change, and event handlers that are data: [[:action/inc]], not closures. No
component-local state anywhere. 43 widget tags so far; the six demos below are two basics
plus four 7GUIs tasks.
📖 Docs & guide
jolt counter |
jolt todo |
jolt crud |
jolt flights |
jolt temperature |
jolt timer |
Immediate-mode GUI, the other shape. raygui-jlt, 24 examples
raygui-jlt
is glitter's opposite number: no retained widget tree at all, just
raygui, raylib's companion GUI library, called
straight over its C ABI through jolt.ffi, one shared bindings namespace under a suite of
small programs. raygui ships as a header only, so there is no library to install: the repo
vendors raygui.h and compiles its own. 24 examples across 7 groups (basics, inputs,
collections, containers, dialogs, color and styling), each with a committed screenshot.
📖 Docs & full gallery
bb style-selector |
bb color-picker |
bb floating-window |
bb sliders |
bb list-view-ex |
bb icon-buttons |
The whole binding rides one signature: a bounding
Rectanglepassed by value, application state through a pointer, anintresult; 61 functions, nearly all shaped the same, no callbacks and no retained state to own. So the lifetimes almost vanish: a single module-level scratchRectangle, rewritten before each control, means no example allocates one inside a frame.GuiScrollPanelis the lone exception: two rectangles in one call, where Clojure's left-to-right evaluation would let the second clobber the first, so it gets a buffer of its own. Screenshots rather than GIFs here, deliberately: synthetic clicks do not actuate a raylib window at all, so a recording could never show a button being pressed.
The same suite, a different Clojure. raygui-jnk, the jank twin
raygui-jnk is raygui-jlt's twin in jank, which compiles Clojure through C++ and LLVM rather than Chez Scheme. Same 24 examples, same names, same descriptions, so the two ports read as one library seen through two implementations. What differs sits underneath: jank reaches raygui as C++ rather than over an FFI, and the binding ships as an installable package. raygui was not available to jank before this, which is why the sibling raylib-jnk substituted keyboard controls in seventeen of its examples. Those can be real controls now, on one dependency line, with no vendored copy and no build script of your own. 📖 Docs & full gallery
bb tab-bar |
bb color-picker-hsv |
bb scroll-panel |
bb gui-state |
bb custom-input-box |
bb spinner-value-box |
Jolt hands raygui a
Rectangleby value. jank cannot, because no native value crosses a jank function boundary at all, so the whole shape inverts. Every wrapper takesx y w has plain numbers and builds itsRectangleinside its own body, never accepting or returning one, and application state lives in boxed cells allocated before the frame loop. A closure counts as a boundary too, soloop/recurdoes work thatdoseqcannot. 53 of raygui's 61 functions are bound that way; most of the rest are font and icon-file calls the six vendored.rgsthemes make unnecessary, since each one carries its own embedded font. The six examples above are deliberately different ones from raygui-jlt's, so between the two sections all 7 groups show.
The same model, on native macOS. glitter-uikit, over AppKit
glitter-uikit
keeps glitter's model exactly as it is and swaps the toolkit underneath it: one
application-state atom, a pure state -> hiccup view, handlers as data, but every widget is a
real AppKit NSView rather than a GTK one. It began as a port of
glimmer-uikit, the AppKit renderer for glitter's
Reagent-style sibling, so both models sit in the same code and the contrast is easy to read.
19 widget tags so far, with the same :hbox/:vbox sugar, and
7GUIs tasks 1 through 6 all ship. That is one further
than glitter itself gets, so Circle Drawer had nothing to port from and its model is original here.
📖 Docs & guide
bb counter |
bb widgets |
bb todo |
bb crud |
bb flights |
bb timer |
The split earns its keep in the demos.
set-temperature,parse-dateandget-view-statecome over from glitter untouched, because they are pure Clojure with no toolkit anywhere in them, so only the view and-mainhad to change. Styling is where the two renderers stop agreeing. AppKit has no CSS classes and no inline style property, so:classand:styleare still accepted and diffed byglitter.core, then land here as honest no-ops. Circle Drawer asked for two things the renderer did not have yet.+[NSEvent mouseLocation]hands back aCGPointby value and no scalar message send can carry a struct, so the FFI layer grew struct-by-value sends.NSStackViewalso lays children out in order with no way to place one freely, so the:canvastag is anNSButtonwhose layer holds the circles. Those circles areCALayers rather than views, and a layer takes no part in hit-testing, which leaves a click free to reach the canvas under a circle and keeps hit-testing a pure function over the model.
And then teaching it. a free course in Lisp and game programming
b12n-gamedev-course is what those 513 raylib examples were for: a free course that teaches Lisp and game programming as one thing, not "game dev, incidentally in Lisp." Six phases take you from never having written a line of Lisp to a capstone, and every lesson builds a real, playable game that teaches a Lisp idiom at the same time. Phase 3 is the one only those three raylib suites make possible: port a game you wrote across all three, and watch the boundary to C move under it. 📖 Read the course
Lesson 1: Pong |
Lesson 2: Breakout |
Lesson 3: Snake |
Lesson 4: Space Invaders |
Lesson 5: Tetris |
Lesson 6: Flappy Bird |
Those are the course's own games, running from its
exercises/code on its own vendored engine: the thing a student actually ends up with, not a borrowed screenshot. Lessons are CC BY-SA 4.0 and the code EPL-2.0, so teaching from it at a meetup or a classroom needs nothing from me. Phases 0 to 3 stand on the three raylib suites today; the later phases open up as the remaining sibling repos do.
The AWS SDK, unmodified. aws-api-jolt
aws-api-jolt runs
the stock Maven release of cognitect-labs/aws-api on
Jolt without forking or patching it. The library supplies the two things Jolt is missing, an HTTP
client and a host-class declaration, and nothing more. Its README is mostly the traps: which
aws-api versions fail on Jolt and why, measured against S3 on a live account, and the one
tools.logging placement that makes nine aws-api namespaces blame each other.
Jolt goes serverless. an AWS Lambda custom runtime
lambda-mvp-jlt runs
Jolt as an AWS Lambda custom runtime, the same
provided.al2023 contract that awslabs/aws-lambda-cpp
implements for C++. The loop itself is small: long-poll an invocation, run the handler, then
POST the response back, and the whole thing comes out to about 60 lines of Clojure over Jolt's
own HTTP client. joltc build compiles the handler and the runtime loop together into one
bootstrap binary, and a jolt bench task compares cold and warm boot time across memory tiers
against your own AWS account, because a number from someone else's account never tells you much.
And the CI half of that. setup-jolt, a GitHub Action
setup-jolt is the
GitHub Action nobody had written yet: install jolt on a runner, whether that's Linux x86_64 or
macOS x86_64 and arm64, straight from its prebuilt releases with the published sha256 checked.
DeLaGuardo/setup-clojure already does the same job
for babashka and the Clojure CLI, so this fills the gap it left. It grew out of lambda-mvp-jlt
wanting its own CI to run through jolt instead of a separate babashka install, and it turned out
useful enough on its own to split out as its own repo.
Every public repo, in one list
champaca (a Java runtime inside Jolt) · jolt-openldk (Java libraries from Jolt through OpenLDK on SBCL) · aws-api-jolt (cognitect aws-api on Jolt, no fork) · mcp-tkx (MCP clients and servers, five protocol revisions) · b12n-gamedev-course · raylib-jlt · raylib-jnk · raylib-clj · raylib-jolt-demo · raylib-jank-demo · raylib-clj-demo (every example, one project each) · raylib-pacman · lisp-games · raylib-ios · raylib-ios-demo (137 scenes on an iPhone) · glitter (Replicant-style GTK4 for Jolt) · glitter-gl (OpenGL geometry and shaders for it) · glitter-uikit (the same model, on native macOS AppKit) · uikit-demo (a small glitter-uikit app with live exchange rates) · glitter-core (glitter's reconciler, with no native deps) · nexus-jolt (cjohansen/nexus, ported to Jolt) · raygui-jlt (immediate-mode GUI for Jolt) · raygui-jnk (the same suite, for jank) · lambda-mvp-jlt (AWS Lambda custom runtime for Jolt) · lambda-mvp-jnk (the same MVP in jank, as a container image) · lambda-mvp-cljs (the same MVP in ClojureScript, on nodejs24.x) · lambda-mvp-rst (the same MVP in Jolt, its JSON built by real Rust via jolt-diplomat) · lambda-mvp-bb (the same MVP in Babashka, via blambda) · setup-jolt (GitHub Action to install jolt) · dartclojure.el (Dart/Flutter → ClojureDart, in Emacs) · viip · Clojars · RubyGems
I started jlt-commons in August 2026, a community-led home for Jolt libraries and tooling.
Why it exists, how projects join, and what's there so far
Jolt is young, so most of its ecosystem is still one person's side project, and a side project stops the month its author gets busy. The org gives that work somewhere to land, and gives anyone who wants to help a place to do it that is not someone else's personal account.
There are two ways in. Adoption is for a project whose maintainer can no longer look after it. We ask the current owner first, every time, and prefer a transfer over a fork so the stars and the issue history come along. Incubation is for new work, whether that is a port of a Clojure library people already rely on or something the core team would rather not own. The bar is a real gap and someone willing to maintain it, which is deliberately low while the language is this young.
It is not an official Jolt project. The language and the libraries its core team maintains live in jolt-lang.
What is there so far:
- raylib-jlt, the raylib bindings and examples above, and the first thing I moved across from my own account. Its docs moved with it, off my own hosting and onto the org's.
- raygui-jlt, the immediate-mode GUI suite, transferred the same day and onto the same docs setup.
- glitter, the GTK4 renderer above, with glitter-gl for OpenGL geometry and shaders and glitter-uikit for the same model on native macOS AppKit. All three went across together and publish at jlt-commons.github.io.
- docs-engine, the generator behind every project's documentation. A project writes markdown and a short config file, and gets syntax highlighting, mermaid diagrams and a theme that follows the reader's light or dark setting, without maintaining any of that itself.
- meta, the governance and the proposal queue, kept short enough that people will actually read it.
- awesome-jolt, the curated list for the whole ecosystem: language and compiler, official libraries, community projects, tooling and docs.
- ci-builds, fleet CI that builds every
project against one jolt version and reports pass or fail in a single table. It exists
because jolt v0.8.0 once reversed an FFI function's argument order, and two projects
floating on
latestbroke silently. - Since September: raylib-jolt-demo, the 187 examples split out of raylib-jlt, and raylib-ios with raylib-ios-demo for the iPhone. aws-api-jolt and jolt-openldk are both from above. glitter-core and nexus-jolt came out of glitter, because Jolt inherits a dependency's native libraries transitively, and glitter-uikit would otherwise refuse to load without a GTK it never calls. uikit-demo puts glitter-uikit to work on a small app. One window ports Learning Cocoa's 2001 currency converter, and another converts against live ECB rates.
The org isn't mostly mine anymore, and that was the point of starting it. It's at 44 public repos now. yogthos has brought in more than I have: ebb, his port of missionary that I've contributed to, ftxui-jolt for terminal UIs over FTXUI, writ, a DSL that blocks AI mistakes through proof, then ensemble for Erlang-style actors, slipstream for GPU vector rendering, jolt-lsp, jolt-nrepl-mcp, upbge-jolt and quite a few more, along with the raylib-android port. A couple are shared work, like jolt-metrics, the code-metrics tool.
If you maintain something in Jolt and would rather it outlived your own free time, or you want to build something and would rather not do it alone, the proposal queue is open.
b12n-oss is where my own open source lands.
The name, the repos, and why only public ones show
It is a
different thing from jlt-commons above, which is community-led and specific to Jolt. This
one is just mine, and the name is a numeronym for burinchoomnuan: the first letter, the
twelve in between, then the last, the same trick as i18n and k8s.
- mcp-tkx, a Clojure and ClojureScript library
for building Model Context Protocol clients and servers. Five protocol revisions from
2024-11-05through2026-07-28, and a single session can serve both the handshake era and the stateless one, so a server does not have to pick a side while its clients catch up. The newest revision drops the handshake entirely and replaces server-initiated requests with multi round-trip results, which is a real shift if you already run a server. It is a fork of Metosin's mcp-toolkit, with the namespaces deliberately unchanged so it stays a drop-in. Runs on the JVM, on Node, and on Babashka. Docs, EPL-2.0. - raygui-jnk and
raylib-jnk, the jank raygui and raylib
suites above, moved here from my personal account and off their standalone
.b12n.appdomains. Docs and docs publish through jlt-commons/docs-engine. - raylib-clj, the JVM Clojure suite
above, moved here the same way. Its docs moved off its own
raylib-clj.b12n.appdomain too; docs publish through jlt-commons/docs-engine the same way. - raylib-jank-demo and raylib-clj-demo, where those two suites' examples went once the binding repos stopped holding them.
- raylib-pacman and lisp-games, the four-runtime comparisons above.
- champaca, the Java runtime inside Jolt, and the newest one here. GPLv3 or later with the Classpath Exception, which it inherits from OpenLDK.
Only the public repos show up on this page. A fair number are still private while they get to a state worth showing, so the list grows as they do.
Two fixes merged into cisco/ChezScheme, the Scheme that Jolt compiles to, and seven into atgreen/openldk. In Jolt itself, 137 commits since July and 79 merged pull requests. Counting wider, 197 pull requests over 52 projects, 170 of them merged.
What each fix was, project by project
The furthest down the stack I've gone is cisco/ChezScheme,
the Scheme that Jolt compiles to. Matthew Flatt merged two of my pull requests there, and
both of them came out of the embedding work further up this page.
#1076 fixes what happens when Chez is embedded
in a process that already handles SIGSEGV, the way .NET and Java runtimes do. Sscheme_init
used to replace the host's handler, so a null dereference on one of the host's own threads
aborted the whole process even though no Scheme code was running. Now the fault goes back to
the handler that was there first.
#1091 repairs where arm64 puts foreign-procedure
arguments once they spill past the registers onto the stack. I checked 3066 generated C
signatures against clang on macOS arm64, and 650 of them came out wrong before the fix and none
after. Its new tests then failed on every x86_64 job, which turned up a second bug on that side,
where a stack struct of an awkward size like 3, 7 or 13 bytes wrote over the next argument. So
the pull request ended up fixing both architectures. A third one, keeping code in a single
aligned region on Apple arm64, got closed upstream, and the same fix landed in Jolt instead.
atgreen/openldk got seven pull
requests from me in October, all merged by Anthony Green. They're what fell out of building
jolt-openldk and then champaca on top of it: a JIT-failure loop, JMOD zip offsets, String
bytes stored signed the way Java stores them, and enough of NativeLibraries.load and zlib's
macOS name for java.nio.file to work on a Mac. Instant.now() needed
VM.getNanoTimeAdjustment, and two threading fixes stopped waiting threads from sleeping
through the wake-up they were waiting for.
jolt-lang/jolt itself is where
most of the volume goes, with 137 commits since July and 79 merged pull requests. The summer
was the FFI and the toolchain: :string carrying NULL in both directions, fiber-parking for
subprocess pipe I/O, a cross-compilation path with a CI cross-smoke workflow behind it, shell
completion in zsh, bash and fish, a rolling nightly prerelease, and jolt build --signable for
an executable codesign --verify --strict will accept. October and November went deeper.
There's a jolt.debug namespace that hands you a throwable's frames along with their locals, an
nREPL *eval-wrapper* hook, and a jolt build --target that compiles for a 32-bit machine's
fixnum width, which is part of how Clojure ended up on a Wii. On top of that came a cheaper
double-to-integer cast that tests the fixnum window inline, plus java.io.RandomAccessFile, a
bundled cheshire.core and a long tail of JVM behaviours matched one at a time. The dot-form
fix from September needed a second patch from the maintainer for the runtime half, which is
still a fair description of how much of the compiler I have left to learn.
Building on it turns up patches next door too, across the jolt-lang org:
nrepl streaming eval output as it
flushes and answering lookup and completion the way editors expect,
ring-chez-adapter verifying client
certificates for mTLS, http-client getting a
working PKCS12 KeyStore, multipart finding its
boundary in linear time, and older ones in
xml, glimmer
and jolt-diplomat.
Counting wider: across public repositories outside my own accounts and organizations, @burinc has 197 pull requests over 52 projects, 170 of them merged. That number needs some unpacking before it means much. 31 are the Clojure Civitas articles listed above, which land as pull requests to the site. Another 106 are the jolt-lang work, spread across 13 of its repos, and that part has more than tripled since September. That leaves 60 across 38 other projects, and those are the ones I would actually call upstream: nearly always a bug I hit while building something else, filed against the tool that hit it. cisco/ChezScheme, atgreen/openldk, weavejester/cljfmt, clj-commons/antq, seancorfield/deps-new, djblue/portal, babashka/process, babashka/pod-babashka-aws, funcool/promesa, lambdaisland/classpath, zero-one-group/geni, jank-lang/jank and raysan5/raylib are the names a Clojure reader is most likely to know. Four in five of the 197 landed in 2025 and 2026.
Still open: bhauman/clojure-mcp-light#33
teaches its file-extension and nREPL-process detection to recognize .jolt files and jolt's own
REPL, alongside a timbre config fix. clojure-mcp-light is where clj-nrepl-eval comes from, so
once it lands, Jolt gets the same LLM-assisted REPL workflow Clojure already has.
Longer-running: contributions to babashka/process, bhauman/clojure-mcp, zero-one-group/geni, BrunoBonacci/graalvm-clojure, yogthos/Selmer, clojure-emacs/clomacs, dundalek/closh, juxt/mach, apache/incubator-mxnet, and others.
📌 Two handles, one person. @agilecreativity is my original account, opened in 2013 and mostly Ruby until I found Clojure in early 2016. Both eras live there, which is why the list above points at both handles. @burinc is where I work now.
| 🌐 Blog | b12n.net |
| in/burinc | |
| 🐦 X | @agilecreativity |
| ✍️ Articles | Clojure Civitas |
| 🐙 GitHub | @burinc · @agilecreativity |
Lisp · Clojure · ClojureDart · Emacs · and a stubborn belief that small tools compound.