glitter-gl builds solids out of vectors and matrices, and shader programs out of maps describing their uniforms and statements: ordinary Clojure data, tessellated to a GL vertex buffer or emitted as GLSL only when it actually has to run. It's written for Jolt, native Clojure on Chez Scheme: no JVM, no boxing, just OpenGL and your data.
The plasma demo: a domain-warped plasma shader blended
with animated gold stripes, composed from two independent shader
modules and lit with a simple diffuse term, wired straight to
:gl-area. The guide has
three more takes on the same
shader, including flat shading on a cube and a tetrahedron.
flowchart LR
subgraph pure["24 files: no dependency on glitter at all"]
geom["vector · vec2 · matrix · quaternion
aabb · rect · circle · line · plane
triangle · sphere · polygon
bezier · intersect"]
mesh["mesh · glmesh
primitives · polyhedra"]
glplumb["shader · gl
offscreen · renderer"]
scene["scene.clj
scene tree to render plan"]
app["app.clj
reactive-area builds the
:gl-area prop map"]
end
gtk["gtk.clj
the one file"]
glitter["glitter
widget registry"]
geom --> mesh
mesh --> glplumb
geom --> scene
glplumb --> app
scene --> app
gtk --> app
glitter -.->|"required by gtk.clj alone,
1 of 25 namespaces"| gtk
Of the 25 namespaces under src/glitter_gl/, only
gtk.clj has a literal :require on
glitter.*. The geometry, matrix, mesh, and shader halves
run from any Jolt program that has an OpenGL context: pull in
glitter-gl.gtk only if you also want a
:gl-area pane in a glitter UI.
A shader is a map declaring its interface: uniforms, attributes, varyings, and a GLSL version. Bodies are vectors of statements built from expression nodes, and independent modules merge with ordinary map operations before anything touches the GPU:
{:version "330 core"
:uniforms {:u_mvp :mat4, :u_time [:float 0.0]} ; type, or [type default]
:attribs {:a_pos [:vec3 0], :a_normal [:vec3 1]} ; [type location]
:varying {:v_normal :vec3} ; out in vs, in in fs
:fs-out {:frag_color :vec4}
:vs-main [[:set :gl_Position [:* :u_mvp [:vec4 :a_pos 1.0]]]]
:fs-main [[:let :n :vec3 [:normalize :v_normal]] ...]}
The uniform/in/out
declarations are generated from the maps, and merge-specs
combines modules (a plasma term, a stripe term, a lighting term) into
one program. The GLSL string is only produced when
program compiles it.
Solids are built from glitter-gl.primitives (cuboid, tetrahedron, sphere, plane) as plain Clojure meshes, transformed and combined with ordinary functions, then tessellated to an interleaved position+normal float buffer for the GPU.
Requiring glitter-gl.gtk registers a :gl-area tag into glitter's widget registry, so a GtkGLArea reconciles in the same hiccup tree as every other widget.
22 of 25 namespaces are verbatim ports (geometry, matrices, meshes, the shader DSL, raw GL bindings) via glimmer-gl from thi.ng/geom; 3 are adapted for glitter's single state atom.
177 tests, 556 assertions, plus a live-GTK smoke that drives a real GtkGLArea under the actual reconciler rather than a fake renderer.
jolt -M:plasma # rotating cube/sphere/tetra + composable plasma/stripes shader
jolt -M:test # unit suite: 177 tests, 556 assertions
jolt -M:check # headless sanity check: shader compiles, geometry buffers valid
jolt -M:gl-area-smoke # live-GTK smoke: :gl-area construct/realize/render/resize
Requires Jolt (tested against v0.7.27), GTK4, and a native OpenGL context. Released under Apache 2.0. Ported from glimmer-gl, which does the same for glitter's Reagent-style sibling glimmer, and which itself ports thi.ng/geom. See the guide for the full nine-page tour.