Home
📚

ShaderStack

I’ve been working on a Concatenative Programming Language which compiles to GLSL for writing fragment shaders. You can play with it at shaders.jordanscales.com.
Concatenative programming languages (sometimes referred to as “catlangs”) have an elegant syntax for function composition: placing words next to each other.
// An example in JavaScript
translateX(scale(p, 2), -1)
! An example in factorcode.org
p 2 scale -1 translateX
I’m not very experienced with writing fragment shaders, but some of my favorite examples include 🎥 composing various mathematical functions in elegant ways.
So, a language where writing composition is pleasant means writing shaders is pleasant. That’s the idea, anyway.
As an example, here’s a solid color in RGBA format.
Toggle shader source Remix this
: main 0.4 0.71 1.0 1.0 vec4 ;
This is roughly equivalent to the following JavaScript.
function main() {
  return vec4(0.4, 0.61, 1.0, 1.0);
}
If I want to adjust the blue channel based on the mouse coordinates (or taps on a mobile device), I need to compose vec4 with a mouse position. (Technically, with mouse and resolution getters, and an x-coordinate function)
function main() {
  return vec4(0.4, 0.61, getX(mouse() / resolution()), 1.0);
}
With concatenation this is more elegant.
Toggle shader source Remix this
: main
  0.4   \ R
  0.71  \ G
  
  \ B 
  mouse resolution / .x
  
  1.0   \ A
  vec4
;
And I can extend this further to display a circle with this color, rather than the whole screen. Once I have the “word” to draw a circle, I just shove it in the middle of my program and take the minimum.
Toggle shader source Remix this

:: circle ( p r -- value )
   p length
   r
   step
;

: main
  0.4   \ R
  0.71  \ G
  
  \ B 
  mouse resolution / .x
  vec3  \ RGB
  
  \ 1 if inside the circle, 0 if outside
  uv 0.5 circle
  
  \ Lesser of two vectors
  min
  
  1.0   \ A
  vec4
;
Thanks for reading! Be sure me know if you make anything cool.
Toggle shader source Remix this

:: mandel-once ( a b a0 b0 -- a b )
   \ z => z^2 + c
   \ => (a + bi)^2 + (a0 + b0i)
   \ => a^2 + 2abi - b^2 + a0 + b0i
   \ => a^2 - b^2 + a0 + 2abi + b0i

   \ a^2 - b^2 + a0
   a a * b b * - a0 +
   \ 2ab + b0i
   2 a * b * b0 +
;

:: mandel ( a b iter -- length )
   0.0 0.0
   iter [ a b mandel-once ] times
   vec2 length
;

: main
  \ -2 to +2
  uv 2.0 * dup .x swap .y
  \ run 20 iterations
  20 mandel
  mouse resolution / .x +
  mouse resolution / .y +
  palette
  1.0 vec4
;