Liking cljdoc? Tell your friends :D

warpaint

A Clojure DSL for writing GLSL 450 shaders as s-expressions, with in-process SPIR-V compilation via lwjgl-shaderc.

Write your shaders in Clojure. Get SPIR-V out.

Clojars Project

Installation

;; project.clj
[com.exokomodo/warpaint "0.1.0"]
;; deps.edn
com.exokomodo/warpaint {:mvn/version "0.1.0"}

Quick Start

(require '[warpaint.dsl :refer [defshader emit-glsl compile-shader]])

;; Define a shader — 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 a java.nio.ByteBuffer containing SPIR-V
;; Pass it wherever you'd pass a VkShaderModuleCreateInfo buffer

API

defshader

Defines a named var. Compiles to SPIR-V at load time.

(defshader name :stage descriptor & body-forms)

compile-shader

Compile a descriptor map at runtime. Returns a java.nio.ByteBuffer.

(compile-shader {:stage :vertex :inputs [...] :main [...]})

emit-glsl

Emit GLSL source as a string without compiling. Useful for debugging.

(emit-glsl {:stage :vertex :inputs [...] :main [...]})
;; => "#version 450\n..."

load-edn

Load a shader descriptor from an EDN file and compile it. Stage is inferred from the file extension (.vert / .frag / .comp).

(load-edn "shaders/my-shader.vert.edn")

Shader Descriptor Map

KeyDescription
:stage:vertex | :fragment | :compute
:inputs[{:name :kw :type :glsl-type :location N}]
:outputs[{:name :kw :type :glsl-type :location N}]
:push-constants[{:name :kw :type :glsl-type}] — emitted as pc block
:uniforms[{:name :kw :type :glsl-type :set N :binding N}]
:const-arrays[{:name :kw :type :glsl-type :size N :values [...]}]
:mains-expression forms for void main()

Supported GLSL Types

:float :int :uint :bool :vec2 :vec3 :vec4 :mat2 :mat3 :mat4 :ivec2 :ivec3 :ivec4 :sampler2D

Expression Reference

ClojureGLSL
(+ a b)(a + b)
(- a b)(a - b)
(* a b)(a * b)
(/ a b)(a / b)
(= a b)(a == b)
(not= a b)(a != b)
(< a b)(a < b)
(and a b)(a && b)
(or a b)(a \|\| b)
(not a)(!a)
(.x v)v.x
(.translation pc)pc.translation
(.uv-pos v)v.uvPos
(aget arr i)arr[i]
(set! dest val)dest = val
(vec4 x y z w)vec4(x, y, z, w)
(normalize v)normalize(v)
(cos x)cos(x)
(if cond a b)(cond ? a : b)
(when cond body…)if (cond) { … }
(do stmts…)statements
(let [^T n v] …)T n = v; …
gl-Positiongl_Position
gl-VertexIndexgl_VertexIndex
gl-FragCoordgl_FragCoord
gl-PointSizegl_PointSize

Identifiers follow kebab→camelCase: :frag-colorfragColor.

let bindings require type hints

;; ✅ correct
(let [^float c (cos x)
      ^vec2  p (vec2 0.0 0.0)]
  ...)

;; ❌ will throw — type hint required
(let [c (cos x)] ...)

EDN Shader Format

You can keep shaders as EDN data files:

;; shaders/my-shader.vert.edn
{:inputs  [{:name :in-pos :type :vec2 :location 0}]
 :outputs [{:name :frag-color :type :vec4 :location 0}]
 :push-constants [{:name :color :type :vec4}]
 :main [(set! gl-Position (vec4 (.x in-pos) (.y in-pos) 0.0 1.0))
        (set! frag-color (.color pc))]}
(def my-vert (warpaint.dsl/load-edn "shaders/my-shader.vert.edn"))

Compilation Backend

warpaint uses lwjgl-shaderc for in-process GLSL → SPIR-V compilation with no subprocess overhead. If shaderc natives are unavailable on your platform, it falls back to invoking glslc as a subprocess (from glslang-tools).

Native classifiers are included for:

  • Linux x86_64
  • Linux aarch64 (Raspberry Pi, AWS Graviton)
  • macOS Intel
  • macOS Apple Silicon

License

CC0 1.0 Universal — public domain.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close