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.
Here are some individual pieces I’ve made:
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
;