Week 9 – Justine

For this week’s homework, I followed a tutorial by 3dEx on How to make a Stylized Medieval Wall. I wanted to go with a tutorial for a stylized material because I always find them really cute. Also, I didn’t want to get too caught up in the details of trying to get my output as realistic as possible, especially since this is just my first dip into Substance Designer.

I’ve had several prior experienced with node-based programming, so the concept wasn’t new to me. However, as with any new software, I’m still at the point where I’m intimidated by how many nodes there are that I still don’t know exist or how to use. Despite that, this week’s homework was a great warm-up exercise to get me familiar with some of the potential nodes I could use for my final project.

I found it very useful that there are so many nodes, not just for blending calculations but also texture generators for various kinds of noises and patterns. This highly streamlines what would have taken forever when we were coding with only GLSL, so I definitely see the potential of how Substance Designer can help you make many materials–and fast! I also found it very useful to see how vital shapes and shaping functions are, especially with how they’re used for almost everything throughout the graph.

Below is a rendering my final material!


Week 8 – Justine

My diorama for this week’s homework features two versions of Marcel Breuer’s The Wassily Chair: one that is fairly brand new, and another that is very dirty, old, and worn. The model was borrowed from a good friend of mine, made for a 3D asset class at her college. The files she sent me already came textured, so I just swapped them out with various materials from freepbr.com for the sake of this exercise. The skybox is the Unfinished Office HDRI from polyhaven.com. I liked that the environment was plain and simple with a single main source of light (the window) for the metal on the chairs to reflect.


The first chair uses the Black Leather PBR Material for the seating fabric,  the Scuffed Iron PBR Metal Material for the chair frame, and the Base White Tile PBR Material for a sort of pedestal. This combination of materials is meant to resemble the Wassily Chair in its original condition. However, the metal isn’t perfectly polished since there is a little bit of noticeable scuff marks, perhaps a sign of wear from being moved around inside a home.

On the other hand, the second chair uses the Dirty Office Fabric PBR Material for the seating fabric, the Used Stainless Steel PBR Material for the chair frame, and the Matted Old Shaggy Rug PBR Material for the small circular carpet underneath. This was meant to be a very unflattering combination of materials to contrast the sleekness and cleanliness of the first chair.

I also find it ironic if this second version was to be on display in a museum side by side with the first chair–I like how just the materials alone can tell stories about the object and get you wondering how it has been used or taken care of throughout its existence.


For the emissive material, I first experimented with placing it on a simple sphere. Since the two chairs are largely made of metal, I moved the sphere in between them to hopefully get some cool reflections. I did a little bit of research and got some post-processing on the camera using Global Volume. I used a little bit of Bloom to get some pretty glow, not just on the emissive material but also on the skybox window light.

However, I ended up having problems with actually getting the emissive material to shine its light onto surrounding objects. I did some more research and pretty much all of them mentioned turning off Global Illumination and baking the light for it to show up. I tried playing around with those two things but nothing seemed to work. I don’t have much experience with how lighting works in 3D, so there was probably a step or setting I overlooked. I would probably do an isolated test in a fresh Unity project next time to try and figure it out, in case I want to use emissive materials in my projects in the future.


Midterm Assignment – Justine

“Digital Mirror” Artwork: Crappy Old TV Shader

Artistic/Design Inspirations

For my midterm assignment, I decided to go down the “digital mirror” route. When I was first learning how to bring my webcam feed from the .js file to the .frag file, I incorporated the code from my week 3 homework to test out how it would look. I ended up really liking how it looked and I thought about building upon it to strengthen the visual communication. I did some more research into retro shaders and came across Retro CRT Shader Breakdown by Cyanilux. This was really helpful at actually picking apart/deconstructing the different elements present in the display of CRT monitors and worked as a great place to start.

Aesthetic Goals for the Project + How I Achieved Them

This project involved a lot of trial and error, tweaking values little by little until I got what I wanted! However, referencing the Retro CRT Shader Breakdown guided me in the logic/approaches/steps I needed to take to slowly form each of the components of the shader.

I pretty much used exactly the same code for the CRT shader, apart from increasing the tiling size from 50.0 to 150.0. It seemed appropriate since the resolution of the webcam feed would be much more than the size of the canvas in the Book of Shaders editor. The scan lines effect is generated with a high frequency sin( ) wave that travels up slowly using uTime. Meanwhile, the static effect is made with simple digital randomness, interpolating based on uTime so that the randomness appears to dance around.


Below are screenshots of the scrolling effect, isolating the different layers that go into it. The left image is the base gradient that travels down based on uTime, the middle image is multiplied with the randomness static, and the right image is after it is sent through the hsb2rgb( ) function.

screen-shot-2023-02-19-at-7-45-54-am screen-shot-2023-02-19-at-7-46-13-am screen-shot-2023-02-19-at-7-46-34-am

All of these vec3’s come together at the very end to apply onto the webcam feed! This is also where I learned of the difference between multiplying and dividing colours. For example, multiplying worked well with the scan lines effect since the vec3 was mostly white, but doing so with the scrolling effect would not work well since it would produce a darker image instead.

Creative Decisions Along the Way

I thought that this project would be more fun with a little bit of interactivity!

I used p5.AudioIn( ) which by default gets the audio input from your computer’s microphone. I then used the .getLevel( ) method to measure the volume of the audio input (which I learned gives an output ranging from 0 to 1) and used map( ) to increase that range by a little bit. I use that volume level to displace the webcam feed on the x-axis, so the louder you speak, the more crazily the video moves side to side.

I felt that it was too much going on when all of the different effects are applied together, so I decided to instead make two versions that the user can toggle between. Both versions use the scrolling effect with the webcam feed, but the first one includes the CRT shader while the second one includes the horizontal scan lines.

During the presentation, I had the interaction to toggle between shader effects set to mouseIsPressed. I forgot that the project had to run fullscreen in the browser, so I implemented that and switched the shader toggle to keyIsDown(32) instead (32 = key code for the spacebar). I also tweaked the fullscreen template code a little bit so that you can toggle back and forth between fullscreen and a regular sized window.

Final Result



Click here to go to the p5.js sketch!

Notes for interaction:

  • Click on the canvas to toggle fullscreen mode.
  • Hold spacebar to toggle between shader effects.
  • Speak into your microphone to affect the displacement of the video.

Week 4 – Justine

…super late Week 4 homework post. I think a part of my hesitation with starting this week’s homework was coming back once again to p5.js. I don’t hate it–in fact, I’d say I’m pretty comfortable navigating through p5 at this point, especially since I’ve been using it in multiple classes since my first year. However, bringing in shaders and learning how the .js file and .frag files talk to each other was quite intimidating (but I figured it out!)

Pixel Manipulation

The image I decided to use for this week’s homework is a random image I found on my computer: Gru from Despicable Me, dramatically posing inside a room at sunset. This is an edit I made a few years ago for my friend, but I don’t remember at all why. Anyway, I thought that I could try and be creative and give purpose to the specific image I chose.


I wanted the image to be affected more when the cursor gets closer to Gru’s hand. By measuring the distance of MouseX and MouseY to the coordinates of the hand using dist( ), I then map( )‘ed those values to range between 1.0 and 0.0. When the cursor is too far away, the value will be 0.0 and nothing happens. When the cursor approaches the hand, the value will increase until it reaches 1.0 and thus the image will experience stronger displacement.

Below is the second version of Gru. I made this by multiplying the original texture by a red colour, then a gradient that mixes to orange, then a gradient that mixes to yellow. Typical fire colours. I used pow( ) for the mix( ) interpolation so that the gradients don’t block too much of the main image. I also lowered the alpha for the yellow gradient so that a bit of white peeks in at the very bottom, like the hottest part of a flame.


The interpolation between the above two versions is in combination with the 2D Simplex Noise function from Patricio Gonzlez Vivo, borrowed from the Pixel Displacement example from this week. I greatly reduced the amplitude of the offset and bumped up the frequency to so that the effect would be in tiny but quick waves. I wanted the image to still be somewhat recognizable afterwards yet convey the type of warping we tend to see in the air from open flames.

Below is a demonstration of the final result:


Click here to go to the p5.js sketch!

Overall, I think that mixing between the warm colours really helps to communicate the image of fire, but I feel like the noise on its own makes it look more like water. If I were to improve on this, I would probably take the time to play around and possibly develop own noise function that has sharper and more erratic movements, much like actual fire.

Week 3 – Justine

For this week’s shader homework, I was inspired by the image below and wanted to recreate the TV CRT. I thought it was a good combination of things we learned during week 3: namely creating and positioning shapes, as well as making those shapes repeat in patterns.


I began with drawing three rectangles using the drawRectangle( ) function we made during class. I then applied a red, green, and blue colour to each of the rectangles, resulting in the base pattern below.

I used a function called brickTile( ) introduced in the Book of Shaders Chapter 9: Patterns that would tile and offset this into a brick-like pattern (staggered every other row/column). I could manipulate the “zoom” in the function which affects how small each tile will appear.

screen-shot-2023-02-02-at-10-13-07-pm  screen-shot-2023-02-02-at-10-20-44-pm  screen-shot-2023-02-03-at-12-19-42-am

(changing the zoom variable from 1.0, to 4.0, to 20.0).

As you can see, the smaller the pattern, the more it resembles old school TV’s–they remind me of when I was a kid and I would stick my face so close to the TV that I could see the individual reds, greens and blues.

Now that I had the TV CRT pattern working, I thought to try and make it display images like a TV. I wanted this to work so that with a black and white image such as a white circle on a dark background, this would correspond with how bright the CRTs will display. This was done by multiplying my desired image to be displayed with the CRT rectangles. I also played around a little bit with the variables in the circle( ) function from the Book of Shaders Chapter 7: Shapes so that I could see a more feathered edge with the smoothstep( ) method, as well as to lighten the background from a pure black… this helped me to better visualize how the brightness becomes affected.

I also brought in my HSB rainbow wave code from week 2 to use as my displayed image, and I was pleasantly surprised to see that it worked perfectly on my first try. I was afraid that the rainbow wave being HSB would interfere with the CRTs being drawn with RGB, but I guess it makes sense that it doesn’t interfere since they are two separate vec3’s.


This was a very simple experimentation with shapes and patterns but I am quite happy with how effective the results are. It was a good reminder of how awesome the power of RGB is, since I am able to see the yellows, cyans and magentas despite none of those colours technically getting drawn.

Week 2 – Justine

Homework Exercises

This week’s homework focused on the exploration of using HSB values and shaping functions. I had a lot of trouble visualizing the different shaping functions, and most of what I created for this week was through a lot of trial and error—tweaking floats, swapping operations, etc… It was also difficult for me to understand the math, so primarily manipulating the hue helped a lot in the visualization aspect.

I discovered the atan( ) function and I really liked how I was able to make it look like a rainbow wheel, radiating from the lower-left corner of the window. I spent some time playing around with the entire expression and figuring out which variables manipulated what. For example, I learned that anything multiplied or divided by atan( ) affected the width of the colours, while anything added or subtracted affected the position of the colours.

For this exercise, I added u_time to the expression and slowed it down by a quarter to give the colours a slight rotation. It reminds me of a spinning diamond gem—I played around with using a sine function to decrease the saturation at the top-right of the window, and a linear function that decreases the brightness at the opposite corner. These are meant to look like a highlight and shadow.


vec3 hsb;
hsb.r = atan(st.x/st.y) * 0.5 – (u_time/4.);
hsb.g = 1.0-sin(st.x*st.y);
hsb.b = st.x + st.y;

To add some more complexity to the animation, I experimented with using sin( ) to make the rainbow oscillate back and forth, as opposed to infinitely going in a single direction. The result is a very psychedelic rainbow wave! It looks like the rainbow is getting swept around by the wind, or riding along an ocean current.

The two examples below for this exercise are almost exactly the same code; the second one just has a shorter period and thus a tighter wave.

wave     marble

vec3 hsb;
hsb.r = atan(st.x/st.y) * 0.888 – sin(u_time – st.x * 4.);
hsb.g = 1.;
hsb.b = 1.;

Even though I think the animations of the shader are already cool to look at as they are, I could also take this a step further by adding some randomness through noise. This would create some interesting texture to the shader. I also would want to experiment with using mix( ) which was introduced in week 1, perhaps to play around with the hues that appear and add some variation in the palette apart from just having a looping rainbow.

Week 1 – Justine

First impressions with the medium

One of my biggest struggles I noticed while learning GLSL was getting used to float values, since I haven’t worked with them a lot before. It is quite finicky about having those decimal values and the editor gets mad at me whenever I forget them, so I always have to remind myself to include them. I also found it really helpful while familiarizing myself with the new concepts to actually draw them out on paper and better visualize the relationships between the canvas space, mouse position, time, and all the different colour channels.

Homework exercises

For the first exercise, I wanted to play around with using sine and cos waves to control the RGB channels. I also use time to have these colours interpolate across the canvas in a looping manner, as well as offset some of the time inputs to slightly stagger them. I really liked the look of the different colours slowly sweeping in and out, and since the gradients operate on both the x and y axes, there is a certain moment when the two gradients overlap to create a rainbow of colours. This was a really helpful exercise for review, not only as a kind of generative coding art, but also for reviewing trigonometry concepts I haven’t dealt with since high school and playing around with how they may be applied to create cool stuff.






For the second exercise, I wanted to play around with not only mixing two colours, but also mixing two gradients. Additionally, since I worked with time in the previous exercise, I decided to incorporate mouse position for this one–when the cursor moves left and right across the canvas, the gradient appears to transform from one to another. One way I would improve this if I had more time is to also have the gradient or colours change when the cursor moves up and down on the canvas, rather than only left and right. The way it looks right now reminds me of moving between a sunrise and sunset sky, so I think it would be fitting if the vertical mouse movement would give the appearance of the sun moving up and down the sky with the yellow.