Week 10 Homework

This week I tried using as many ShaderGraph nodes as possible to get familiar with the available functions. I also tried out various post processing effects and ended up using lens distortion, bloom, and some colour processing. For the image texture shader, I added animated noise to a base texture map, as well as a coloured emission.


For the reveal shader, I used the swirl UV node to alter the alpha map, and animated both the swirl and the alpha threshold using a time sine function.


Week 9 Homework

For this week I followed all the prompts to create various simple 3d objects in a scene. I experimented with animating textures and colours in various ways. For example, in the two screenshots below which show the scene at two different points in time, there is a sphere where I animated the colour and intensity mixed with the height map to imitate a glowing hot rock (where the thinnest parts would glow brightest). Also shown below is a cube with vertex displacement according to x position.



Then, I successfully ported over a fragment shader from an earlier week. With the sphere, I added an animation of a moving area of glossiness.


Here is a post processing shader I applied which added a purple hue and sin displacement.



For my midterm project, I made a a audiovisual experience that is partly randomly generated and partly composed. ToneJS was used to create an audio composition of two main parts. P5 was used to communicate with ToneJS and send data to the shader to use to become audioreactive. This consisted of changing opacity that corresponded with when tones are played, as well as a zoom to scale the shader canvas when transitioning to the second part of the composition.

The shader uses shapes, brownian motion, shaping functions, noise, and gradients to create two distinct compositions. While the elements of the first part are all included in the second, the composition changes drastically because the canvas scaling reveals patterns in the elements that were before not visible beyond the frame of the canvas. As well, new elements are added that respond to new audio elements in the second half.


screenshot-2021-03-14-203949 screenshot-2021-03-14-204308

The audiovisual project can be experienced here -> https://trusting-dijkstra-008c11.netlify.app/

The code can be seen here -> https://github.com/srhboo/shader-music

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