Minimal s-expression shader DSL for Clojure.
Emits GLSL 450 source from Clojure data, then compiles to SPIR-V via warpaint.compiler (shaderc in-process, glslc fallback).
(require '[warpaint.dsl :refer [defshader emit-glsl compile-shader]])
;; Define a shader var — compiles to SPIR-V at load time
(defshader my-vert :vertex
{:inputs [{:name :in-pos :type :vec2 :location 0}]
:outputs [{:name :frag-color :type :vec4 :location 0}]
:push-constants [{:name :translation :type :vec2}
{:name :rotation :type :float}
{:name :padding :type :float}
{:name :color :type :vec4}]}
(let [^float c (cos (.rotation pc))
^float s (sin (.rotation pc))
^vec2 rotated (vec2 (- (* (.x in-pos) c) (* (.y in-pos) s))
(+ (* (.x in-pos) s) (* (.y in-pos) c)))
^vec2 final (+ rotated (.translation pc))]
(set! gl-Position (vec4 final 0.0 1.0))
(set! frag-color (.color pc))))
;; my-vert is now a java.nio.ByteBuffer containing SPIR-V
compile-shader and emit-glsl accept a map with these keys:
| Key | Type | Description |
|---|---|---|
:stage | keyword | :vertex | :fragment | :compute |
:inputs | vec | [{:name :kw :type :glsl-type :location N}] |
:outputs | vec | same shape as :inputs |
:push-constants | vec | [{:name :kw :type :glsl-type}] |
:uniforms | vec | [{:name :kw :type :glsl-type :set N :binding N}] |
:const-arrays | vec | [{:name :kw :type :glsl-type :size N :values [...]}] |
:main | vec | s-expression forms for void main() {} |
:float :int :uint :bool :vec2 :vec3 :vec4
:mat2 :mat3 :mat4 :ivec2 :ivec3 :ivec4 :sampler2D
| Clojure form | Emits |
|---|---|
(+ a b) | (a + b) |
(= a b) | (a == b) |
(.x v) | v.x |
(.translation pc) | pc.translation |
(aget arr i) | arr[i] |
(set! dest val) | dest = val |
(vec4 x y z w) | vec4(x, y, z, w) |
(if cond a b) | (cond ? a : b) |
(when cond body…) | if (cond) { … } |
(do stmts…) | statements |
(let [^T n v] …) | T n = v; … |
gl-Position | gl_Position |
gl-VertexIndex | gl_VertexIndex |
gl-FragCoord | gl_FragCoord |
gl-PointSize | gl_PointSize |
Identifiers are kebab→camelCase: :frag-color → fragColor.
Minimal s-expression shader DSL for Clojure.
Emits GLSL 450 source from Clojure data, then compiles to SPIR-V via
warpaint.compiler (shaderc in-process, glslc fallback).
## Quick start
```clojure
(require '[warpaint.dsl :refer [defshader emit-glsl compile-shader]])
;; Define a shader var — compiles to SPIR-V at load time
(defshader my-vert :vertex
{:inputs [{:name :in-pos :type :vec2 :location 0}]
:outputs [{:name :frag-color :type :vec4 :location 0}]
:push-constants [{:name :translation :type :vec2}
{:name :rotation :type :float}
{:name :padding :type :float}
{:name :color :type :vec4}]}
(let [^float c (cos (.rotation pc))
^float s (sin (.rotation pc))
^vec2 rotated (vec2 (- (* (.x in-pos) c) (* (.y in-pos) s))
(+ (* (.x in-pos) s) (* (.y in-pos) c)))
^vec2 final (+ rotated (.translation pc))]
(set! gl-Position (vec4 final 0.0 1.0))
(set! frag-color (.color pc))))
;; my-vert is now a java.nio.ByteBuffer containing SPIR-V
```
## Shader descriptor map
`compile-shader` and `emit-glsl` accept a map with these keys:
| Key | Type | Description |
|-------------------|---------|-------------|
| `:stage` | keyword | `:vertex` \| `:fragment` \| `:compute` |
| `:inputs` | vec | `[{:name :kw :type :glsl-type :location N}]` |
| `:outputs` | vec | same shape as `:inputs` |
| `:push-constants` | vec | `[{:name :kw :type :glsl-type}]` |
| `:uniforms` | vec | `[{:name :kw :type :glsl-type :set N :binding N}]` |
| `:const-arrays` | vec | `[{:name :kw :type :glsl-type :size N :values [...]}]` |
| `:main` | vec | s-expression forms for `void main() {}` |
## Supported GLSL types (as keywords)
`:float` `:int` `:uint` `:bool` `:vec2` `:vec3` `:vec4`
`:mat2` `:mat3` `:mat4` `:ivec2` `:ivec3` `:ivec4` `:sampler2D`
## Expression syntax
| Clojure form | Emits |
|----------------------|------------------|
| `(+ a b)` | `(a + b)` |
| `(= a b)` | `(a == b)` |
| `(.x v)` | `v.x` |
| `(.translation pc)` | `pc.translation` |
| `(aget arr i)` | `arr[i]` |
| `(set! dest val)` | `dest = val` |
| `(vec4 x y z w)` | `vec4(x, y, z, w)` |
| `(if cond a b)` | `(cond ? a : b)` |
| `(when cond body…)` | `if (cond) { … }` |
| `(do stmts…)` | statements |
| `(let [^T n v] …)` | `T n = v; …` |
| `gl-Position` | `gl_Position` |
| `gl-VertexIndex` | `gl_VertexIndex` |
| `gl-FragCoord` | `gl_FragCoord` |
| `gl-PointSize` | `gl_PointSize` |
Identifiers are kebab→camelCase: `:frag-color` → `fragColor`.(compile-shader descriptor)Compile a shader descriptor map to a SPIR-V ByteBuffer. Writes a temporary .glsl file, compiles with shaderc (glslc fallback), and returns a java.nio.ByteBuffer.
See namespace docstring for descriptor map keys.
Compile a shader descriptor map to a SPIR-V ByteBuffer. Writes a temporary .glsl file, compiles with shaderc (glslc fallback), and returns a java.nio.ByteBuffer. See namespace docstring for descriptor map keys.
(defshader shader-name stage descriptor & body)Define a named shader var. Compiles to a SPIR-V ByteBuffer at load time.
Usage: (defshader my-vert :vertex {:inputs [{:name :in-pos :type :vec2 :location 0}] :outputs [{:name :frag-color :type :vec4 :location 0}] :push-constants [{:name :color :type :vec4}]} (set! gl-Position (vec4 (.translation pc) 0.0 1.0)) (set! frag-color (.color pc)))
body forms become the :main of the descriptor.
The resulting var holds a java.nio.ByteBuffer of SPIR-V.
Define a named shader var. Compiles to a SPIR-V ByteBuffer at load time.
Usage:
(defshader my-vert :vertex
{:inputs [{:name :in-pos :type :vec2 :location 0}]
:outputs [{:name :frag-color :type :vec4 :location 0}]
:push-constants [{:name :color :type :vec4}]}
(set! gl-Position (vec4 (.translation pc) 0.0 1.0))
(set! frag-color (.color pc)))
`body` forms become the :main of the descriptor.
The resulting var holds a java.nio.ByteBuffer of SPIR-V.(emit-glsl {:keys [inputs outputs push-constants uniforms const-arrays main]})Emit a GLSL 450 string from a shader descriptor map. Does not compile — useful for inspecting generated source or testing.
See namespace docstring for descriptor map keys.
Emit a GLSL 450 string from a shader descriptor map. Does not compile — useful for inspecting generated source or testing. See namespace docstring for descriptor map keys.
(load-edn path)Load a shader descriptor from an EDN file and compile it to SPIR-V. Stage is inferred from the file extension (.vert / .frag / .comp). Returns a java.nio.ByteBuffer.
Example: (warpaint.dsl/load-edn "shaders/my-shader.vert.edn")
Load a shader descriptor from an EDN file and compile it to SPIR-V. Stage is inferred from the file extension (.vert / .frag / .comp). Returns a java.nio.ByteBuffer. Example: (warpaint.dsl/load-edn "shaders/my-shader.vert.edn")
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |