this is a log of my work on painting-algorithms during recurse center; summer-2026.

read more on the git repo. to understand what the project is about.


260521:

i spent time understanding glslviewer, and getting it set-up for me to use.

this allows me to write shaders on my machine, and render them to a console window — thereby removing the overhead of a browser, javascript, and so on.

i don’t need to rely on any library or environment, or anything. it is just me, and the shader, in the terminal.

wrote the first bit of shader code, as a test template.

flipped uv coordinates so that 0,0 is top left (to make drawing make more sense later).


260528:

discovered step and smoothstep in glsl, instead of writing if else statements. also discovered how combinatorial logic can also result in gates.

for example:

	if (mod(pos.x / size, 2.0)<1.0 && mod(pos.y / size, 2.0)<1.0){
		color = vec3(1.0);
	}
	else{
		color = vec3(0.0);
	}

is the same as:

	float x = step(1.0, mod(pos.x / size, 2.0));
	float y = step(1.0, mod(pos.y / size, 2.0));
 
	float xy = x * y;
 
	color = vec3(xy);
 

step evaluates to 0.0 or 1.0 depending on the bound set.

a shorter version would be:

          vec2 col = step(1.0, mod(pos / size, 2.0))
          color = vec3(col.x * col.y);

260604:

i realized that trying to compute all of this in the shader was sort of stupid.

//auto brush moving; june, 2026.
 
#ifdef GL_ES
	precision mediump float; 
#endif
 
//uniforms: 
uniform vec2 u_resolution;
uniform vec2 u_time; 
uniform int u_frame; 
 
uniform sampler2D u_doubleBuffer0; 
 
//helpers:
float flip(float n){
	return 1.0 - n; 
}
 
//random:
float random(float n){
	float multiplier = 1000.0;
	return(fract(sin(n)*multiplier));
}
 
//noise:
float rand(vec2 p){
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
 
float noise(vec2 p){
    vec2 i = floor(p);
    vec2 f = fract(p);
 
    f = f * f * (3.0 - 2.0 * f);
 
    float a = rand(i);
    float b = rand(i + vec2(1.0, 0.0));
    float c = rand(i + vec2(0.0, 1.0));
    float d = rand(i + vec2(1.0, 1.0));
 
    return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
 
//velocity:
vec2 velocity(vec2 p){
    float n1 = noise(p * 2.0);
    float n2 = noise(p * 2.0 + 10.0);
 
    vec2 v = vec2(n1, n2) * 2.0 - 1.0;
    return v;
}
 
void main(){
 
	//common across passes:
	vec2 uv = gl_FragCoord.xy / u_resolution.xy;
 
	//flip:
	//flip(uv.y);
 
	float col = 0.0; 
	float size = 0.005; 
 
#ifdef DOUBLE_BUFFER_0
	//compute:
	vec4 prev = texture2D(u_doubleBuffer0, uv).rgba; 
	vec2 brush_pos = prev.gb; 
	col = prev.r;
 
	brush_pos = (u_frame==0)
		? vec2 (0.5) 
		: brush_pos + velocity(brush_pos + noise(brush_pos + vec2(u_time)))*0.05;
 
	if (col!=1.0){
		//check if it's near the brush. 
		float d = distance(brush_pos, uv);
		col = step(d,size); 
	}
 
 
	gl_FragColor = vec4(col, vec2(brush_pos), 1.0); 
 
#else
	//shader two: read the r value from the previous run, and use that to paint.
	col = texture2D(u_doubleBuffer0, uv).r; 
	gl_FragColor = vec4(vec3(col), 0.5); 
 
#endif
	//output:
//	gl_FragColor = vec4(r,g,b,a); 
}
 
 

for starters:

glsl does not preserve memory. it simply changes the color of each single pixel via a function that runs constantly. therefore, to preserve a state (or to remember), you use uniforms.


260608:

this past weekend, i fell sick. that was a good thing.

i rethought what i was doing, and how i was spending my time at r.c.