Week 5 HW

For this week’s homework, I worked with Shannen to explore shaders in p5.js. After porting one of my previous shaders to p5.js, I decided to create a shader that distorts an image using shaping functions.

The code for this sketch can be found here: https://editor.p5js.org/Bakaretsu/sketches/zxuKhPC0z

The images I distorted are of Morshu from the Legend of Zelda games. I was inspired by a recent hyper detailed render of him that was posted on the internet, and decided to have these 2 images morph into each other in some way. bts3dho3e7f61tx4xftkd67j2y5zd

I followed the example in the displacement video, and created an offset that changed based on the sin value of time. I multiplied the sin wave to make the warping effect more intense in the x and y directions.

To blend between the 2 images, I used the mix function and passed in time as the value to use to interpolate. This resulted in the 2 images blending between each other over time.

I combined that effect with the displacement shader to create the end result.

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;

Shaders week 5


For this first shader I took inspiration from the webcam displacement example and instead used two images together. I took a picture of a wolf’s head and a picture of water from the internet and added the sin() function to create a murky, watery effect. I used millis() in x position for the set.Uniform statement (myShader.setUniform(“uDisplacement”, map(millis()/50, 0, width, 0, 5));) to produce a u_time effect like in GLSL. In a way, the water image is being animated to displace the wolf image.

#ifdef GL_ES
precision mediump float;

uniform vec2 uMousePosition;
uniform vec2 uResolution;

uniform sampler2D uTexture0; //wolf
uniform sampler2D uTexture1; //water
uniform float uTime;
uniform float uDisplacement;

varying vec2 vTexCoord;

//takes rbg of a pixel and calculates luminosity
float rgb_to_luma( vec3 color ){
return 0.2126*color.r + 0.7152*color.g + 0.0722*color.b;

float map (float value, float min1, float max1, float min2, float max2){

return min2 + (value - min1) * (max2 - min2) / (max1 - min1);

void main() {

vec4 color = vec4(0.0, 1.0, 1.0, 0.0);

vec2 offset = vec2(0.0, 0.0);

offset = texture2D(uTexture0, vec2(vTexCoord.x, 1.0-vTexCoord.y)).gb;
offset *= 1.0;

//colours to luminosity
float c = rgb_to_luma(texture2D(uTexture0, vec2(vTexCoord.x, 1.0-vTexCoord.y)).rgb);

offset = vec2(c, c) + uDisplacement;

vec2 uv = vec2(vTexCoord.x, 1.0-vTexCoord.y) + offset;
//vec2 uv = vec2(uTime(vTexCoord.x), 1.0-vTexCoord.y) + offset;
//using sin() to distort the image
color = texture2D(uTexture1, sin(uv) * 0.5);

gl_FragColor = vec4(color);

Week 5 Shader Explorations


For WEEK 5, I worked with Mahnoor. I was tasked with the initial porting of our Week 3 shaders into p5.js and creating an image distortion shader.

For the image distortion shader, I created a distorter that affects the texture UV coordinates of the image, based on Simple Texture Distort by mikiex on Shader Toy. Instead of using a noise texture as mikiex did, I replaced it with generated simplex noise from the Book of Shaders. To create an inverted colour filter for the image, I divided the initial image that I added the distortion value to the UV with a mix of the distorted initial image texture with an altered second version of the image that instead subtracted the distortion from the UV. Mixing the two versions of the distorted image, one that follows the original colour scheme and another that is inverted distorts them in opposite directions. This results in a swipe effect where the value of distortion would become zero in both directions (looped using sin) and will form the original non-distorted image of the flowers for a split second.

The distorted colours and rounded shapes that the noise forms the image into reminds me of the psychedelic closeups of the surface of a soap bubble (caused by a two-beam interference of light).

Overall, I enjoyed the Shader exercises this week and had fun seeing how my partner, Mahnoor, approached shader art. It was also great to have immediate feedback during the process and to support each other and help when we got stuck.

There is not much to say about porting our Week 3 shaders into p5.js, other than every line of setup code counts. It was more complicated than I thought it would be, although fairly straightforward to execute once you are used to the process. I did have fun applying the shaders as textures to some simple (default) shapes (a sphere and box). I added simple interaction to the p5.js sketch to switch between 3D shapes and shaders on mouse click as well allow the viewer to rotate the shapes using mouse coordinates.

//Author: Madelaine Fischer-Bernhut
//Title: Image Distortion

#ifdef GL_ES
precision mediump float;

//get texture coordinates from vert shader
varying vec2 vTexCoord;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

//texture coming from p5.js
uniform sampler2D imgtext;

//Book of Shaders Simplex Noise (based off Ian McEwan, Ashima Arts)
// Some useful functions
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
float snoise(vec2 v){
 const vec4 C = vec4(0.211324865405187, 0.366025403784439,
 -0.577350269189626, 0.024390243902439);
 vec2 i = floor(v + dot(v, C.yy) );
 vec2 x0 = v - i + dot(i, C.xx);
 vec2 i1;
 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
 vec4 x12 = x0.xyxy + C.xxzz;
 x12.xy -= i1;
 i = mod(i, 289.0);
 vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
 + i.x + vec3(0.0, i1.x, 1.0 ));
 vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
 dot(x12.zw,x12.zw)), 0.0);
 m = m*m ;
 m = m*m ;
 vec3 x = 2.0 * fract(p * C.www) - 1.0;
 vec3 h = abs(x) - 0.5;
 vec3 ox = floor(x + 0.5);
 vec3 a0 = x - ox;
 m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
 vec3 g;
 g.x = a0.x * x0.x + h.x * x0.y;
 g.yz = a0.yz * x12.xz + h.yz * x12.yw;
 return 130.0 * dot(m, g);

void main() {
 // position of the pixel divided by resolution, to get normalized positions on the canvas
 vec2 st = gl_FragCoord.xy/u_resolution.xy; 
 vec2 uv = vTexCoord;
 //texture is loaded upside down and backwards by default, so flip it
 uv.y = 1.0 - uv.y;
 //create a distorter (based off Simple Texture Distort by mikiex, instead of another texture it uses snoise)
 vec2 pos = vec2(st.x*10.0 - (u_time*0.2), st.y*10.0 + (u_time*0.2));
 vec3 dist = vec3(snoise(pos)*0.5+0.5);
 vec2 distorter = vec2(dist.rg) * vec2(0.1-sin(u_time*0.2));
 //zoom in and out of image by multiplying the "UV" value, use fract to tile zoomed out images 
 vec4 imgText = texture2D(imgtext, fract(uv)+distorter);
 vec4 imgText2 = texture2D(imgtext, fract(uv)-distorter);

 float grey = (imgText.r + imgText.g + imgText.b)/3.0;
 vec3 color = vec3(0); 
 //grey filter
 // color = vec3(grey); 
 //default (full) color
 color = vec3(imgText.r,imgText.g, imgText.b); 
 //blend in an "inverted" color filter version of the image
 // color /= mix(color, vec3(1.0), vec3(imgText2.r,imgText2.g, imgText2.b));

 color /= mix(color, vec3((sin(u_time*0.1))), vec3(imgText2.r,imgText2.g, imgText2.b));
 //direct image color
 // gl_FragColor = vec4(imgText);
 //filtered camera feed
 gl_FragColor = vec4(color, 1.0);