Week 5

I worked with Nicole Vella for this week’s homework.

This one which resembles a diffractive glass wall (or kind of like tiny glass brick) is achieved by creating a large grid (with tiny units) where each grid unit has its webcam image shifted slightly to focus on the part where it lies in the greater grid. A brownian noise was added on top by Nicole and then I also added an interactive “clear” spot that can be moved by the mouse. View on p5 here


precision mediump float;

// lets grab texcoords just for fun
varying vec2 vTexCoord;

// our texture coming from p5
uniform sampler2D tex0;
uniform float time;
uniform vec2 uMousePos;

vec3 permute(vec3 x){return mod(((x*34.)+1.)*x,289.);}

float snoise(vec2 v){
const vec4 C=vec4(.211324865405187,.366025403784439,
vec2 i=floor(v+dot(v,C.yy));
vec2 x0=v-i+dot(i,C.xx);
vec2 i1;
vec4 x12=x0.xyxy+C.xxzz;
vec3 p=permute(permute(i.y+vec3(0.,i1.y,1.))
vec3 m=max(.5-vec3(dot(x0,x0),dot(x12.xy,x12.xy),
vec3 x=2.*fract(p*C.www)-1.;
vec3 h=abs(x)-.5;
vec3 ox=floor(x+.5);
vec3 a0=x-ox;
vec3 g;
return 130.*dot(m,g);

float drawCircle( vec2 st, vec2 pos, float size ){

float result = distance( st, pos);
result = smoothstep( result, result + 0.1 * (sin(time* 2.) * 0.5 + 0.5), 0.15);

return result;

vec2 createGrid( in vec2 st, in vec2 grid, out vec2 indices) {

st *= grid;

indices = floor(st);
st = fract(st);

return st;

void main() {

vec2 uv = vTexCoord;
// the texture is loaded upside down and backwards by default so lets flip it
uv = 1.0 - uv;
vec2 st = uv;
vec2 test = uv;
vec2 indices;
st = createGrid(st, vec2(100.), indices);

float n=snoise(vTexCoord+time*0.3)*.5+.5;

// add the distortion to our texture coordinates
vec4 tex = texture2D(tex0, uv + st / 9. * n);
float c = drawCircle(test, uMousePos, 0.05);

vec2 toCenter = vec2(0.5)-st;
float angle = atan(toCenter.y,toCenter.x);
float radius = length(toCenter)*2.0;
float z = radius * 10.*sin(time/1.+ atan(st.y, st.x));

vec4 originalTex = texture2D(tex0, test);
tex = mix(tex, originalTex, c);

gl_FragColor = tex;

The second randomly samples a gridded colour palette to create multiple moving shapes which are then mixed using brownian motion.
The grid size of the palette needs to be specified, and it will generate a different result every time. View on p5 here



precision mediump float;

// lets grab texcoords just for fun
varying vec2 vTexCoord;

// our texture coming from p5
uniform sampler2D u_tex0;
uniform float u_time;
uniform vec2 u_mouse;
uniform vec2 u_resolution;
uniform vec2 randCoord1;
uniform vec2 randCoord2;
uniform vec2 randCoord3;
uniform vec2 randCoord4;
uniform vec2 randCoord5;
uniform vec2 randCoord6;
uniform vec2 randCoord7;
uniform float gridSize;

float random (in vec2 st) {
return fract(sin(dot(st.xy,

// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);

// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));

vec2 u = f * f * (3.0 - 2.0 * f);

return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;

#define OCTAVES 6
float fbm (in vec2 st) {
// Initial values
float value = 0.0;
float amplitude = .5;
float frequency = 0.;
// Loop of octaves
for (int i = 0; i < OCTAVES; i++) {
value += amplitude * noise(st);
st *= 2.;
amplitude *= .5;
return value;

float drawCircle(in vec2 st, in float radius, vec2 pos){
vec2 dist = st-vec2(pos);
return 1.-smoothstep(radius-(radius*0.01),

float drawBlurryCircle(in vec2 st, in float radius, vec2 pos){
vec2 dist = st-vec2(pos);
return 1.-smoothstep(radius-(radius*0.3),

vec4 sampleRandomColour(vec2 randCoord, vec2 uv) {
// set up uv fields to sample colours from the image grid. divide by the cells per side so that it 'zooms' up on one colour
vec2 colourField = uv / gridSize;
// the coords provided need to be normalized
// to shift the canvas the right amount
// to focus on a single random colour
colourField += randCoord / gridSize;
// sample the colour from the image texture
return texture2D(u_tex0, colourField);

void main() {

vec2 uv = vTexCoord;

// flip because it's backwards
uv.y = 1.0 - uv.y;

vec4 colour;

vec4 colour1 = sampleRandomColour(randCoord1, uv);
vec4 colour2 = sampleRandomColour(randCoord2, uv);
vec4 colour3 = sampleRandomColour(randCoord3, uv);
vec4 colour4 = sampleRandomColour(randCoord4, uv);
vec4 colour5 = sampleRandomColour(randCoord5, uv);
vec4 colour6 = sampleRandomColour(randCoord6, uv);
vec4 colour7 = sampleRandomColour(randCoord7, uv);

uv += fbm(uv * 3.0);
uv -= 0.3;
uv /= 2.;

vec2 circleField = uv;
circleField.y += sin(sin(u_time) * uv.x * 20.) * 0.1;

float c = drawBlurryCircle(circleField, 0.3, vec2(0.3 + (sin(u_time) *0.5 + 0.5) * 0.2, 0.4));

vec2 circleField2 = uv;
circleField2.x *= 2.0;
circleField2.x += cos(sin(u_time / 4.) * uv.x * 100.) * 0.1;
float c2 = drawBlurryCircle(circleField2, 0.3, vec2(0.5 + (sin(u_time) *0.5 + 0.5) * 0.2, 0.6));

vec2 circleField3 = uv;
circleField3.x *= 3.0;
circleField3.y += sin(sin(u_time / 6.) * uv.x * 50.) * 0.1;
float c3 = drawBlurryCircle(circleField3, 0.7, vec2(0.1 + (sin(u_time) *0.5 + 0.5), 0.8 - (sin(u_time)*0.5 + 0.5)));

vec2 circleField4 = uv;
circleField4.y *= 5.0;
circleField4.x += sin(cos(u_time / 6.) * uv.x * 50.) * 0.1;
float c4 = drawBlurryCircle(circleField4, 0.2, vec2(0.3 + (sin(u_time) *0.5 + 0.5), 0.7 + (sin(u_time)*0.5 + 0.5)));

vec2 circleField5 = uv;
circleField5.y *= 5.0;
circleField5.x += sin(cos(u_time / 3.) * uv.x * 50.) * 0.1;
float c5 = drawCircle(circleField5, 0.4, u_mouse + vec2(0.0, 1.0));

colour = mix(colour1, colour2, dot(uv.x * 2., uv.y * 2.));
colour = mix(colour, colour3, c3);
colour = mix(colour, colour4, c);
colour = mix(colour, colour5, c2);
colour = mix(colour, colour6, c4);
colour = mix(colour, colour7, c5);

gl_FragColor = colour;

Week 3 Shaders

I used the sample 2d noise from the book of shaders to mix into my existing shader, and applied sin and cos functions to add movement to its position. As for scaling the canvas, I kept it low at 4 times. Combined with the existing time dependent shaping functions I had used previously, they formed soft organic looking shapes that slowly evolved and morphed and moved. Together with the back and forth motion, I think it really captures the feeling of a living organism – almost as if it’s breathing. You can view a live example here


#ifdef GL_ES
precision mediump float;

#define PI 3.14159265359
#define TWO_PI 6.28318530718
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

// random and noise code from https://thebookofshaders.com/11/
float random(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);

float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    // Smooth Interpolation

    // Cubic Hermine Curve.  Same as SmoothStep()
    vec2 u = f*f*(3.0-2.0*f);
    // u = smoothstep(0.,1.,f);

    // Mix 4 coorners percentages
    return mix(a, b, u.x) +
            (c - a)* u.y * (1.0 - u.x) +
            (d - b) * u.x * u.y;

float plot(vec2 st, float pct){
  return  smoothstep( pct-0.1, pct, st.y) -
          smoothstep( pct, pct+0.5, st.y);

//  Function from Iñigo Quiles
//  https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
                     1.0 );
    rgb = rgb*rgb*(3.0-2.0*rgb);
    return c.z * mix( vec3(1.0), rgb, c.y);

vec3 rgb2hsb( in vec3 c ){
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz),
                 vec4(c.gb, K.xy),
                 step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r),
                 vec4(c.r, p.yzx),
                 step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
                d / (q.x + e),

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution;

    // time dependent shaping equations
    float y = sin(PI*st.x+u_time/40.)/0.8 + cos(PI*st.y+ u_time/40.)/2.;
    float yz = sin(PI*st.x+u_time/30.)/1. + cos(PI*st.y+ u_time/10.)/0.8;

    // various colours to mix
    vec3 color = vec3(y);
    vec3 colorA = vec3(0.545,0.169,0.000);
    vec3 colorB = vec3(0.565,0.435,0.121);
    vec3 colorC = vec3(0.126,0.490,0.171);
    vec3 colorD = vec3(0.362,0.237,0.820);
    float pct = plot(st,y);

    // Use polar coordinates instead of cartesian
    vec2 toCenter = vec2(0.5)-st;
    float angle = atan(toCenter.y,toCenter.x);
    float radius = length(toCenter)*2.0;
    // circle equation
    float z = radius * 10.*sin(u_time/2.+ atan(st.y, st.x));

    // scale the canvas for noise
    vec2 pos = vec2(st*4.);
    // move the noise values back and forth
    float n = noise(pos + sin(u_time) + cos(u_time));

    // now mix it all
    color = mix(colorA, colorB, y);
    color = mix(color, colorC, n);
    color = mix(color, colorD, yz);
    vec3 hsbtemp = rgb2hsb(color);
    color = hsb2rgb(vec3(hsbtemp[0], hsbtemp[1]*0.5, hsbtemp[2]*0.5));

    gl_FragColor = vec4(color,1.0);

I’ve been trying to get a sense of how scaling the canvas changes the overall noise effect. As the canvas is scaled larger, many familiar noise textures seem to emerge (pixelation, white noise, stripes like on a tuning VHS). I think it’s interesting that artificially adding noise in these ways provides windows into understanding noise found in the imperfect conversions between mediums (ex analog to digital).


Week 2 Shaders

Note: someone please tell me how to embed the code in these posts because it seems I am the only one who can’t figure it out, and here I am pastebin-ing like a chump. Thanks in advance!


Code: https://pastebin.com/iu7Q6nai

This was an exercise in getting accustomed to using shapes to create images. These are layered circles – including the curves, which are made using background-coloured circles to cover other circles. I found the book of shaders editor’s built in number and colour adjustment tools made it pretty easy to arrange the shapes. In terms of tweaking the circle to take on slightly different curves, I’m still figuring that out.


Code: https://pastebin.com/ndrG1uTa

This is a simple pattern made using a grid. I struggled to figure out how to get access to use the pixel position within the larger canvas, rather than within the grid cell itself. (I still haven’t figured it out). I understand manually designating which row and column combinations to render which set of instructions, but I wonder how to introduce a bit more randomness.

Week 1


Working through the shader examples to really understand how the different functions were composed to create the (seemingly simple) gradients and lines was probably the hardest and more essential part of the process for me. It was difficult for me to wrap my head around mathematical operations on images/colours, particularly in vector form, when I am so used to only thinking about numbers and scalar operations.

From there, I just did a bunch of stepwise experimentation, combining the example functions and using them to mix additional colours one at a time. For the last mix, I did some converting to polar coordinate to create a circular form.

I was so pleasantly surprised about the amount of variation in the end result with the smallest adjustments in time and shaping functions. Since the layering of colours can produce many different hues, and using time dependent gradients will make it so that there are many different intersections of colours, the movement and feeling can change so drastically. It really makes these shaders (which are landed upon with experimentation) feel unique and ephemeral.

link to frag code (I don’t know why this link embed isn’t working): https://pastebin.com/62iqQdjE