# Making Dynamic Animations with Shaders

Alex Trost Originally published at frontend.horse ・6 min read

I’ve been following ilithya’s shader demos for a while and wanted to write about her work weeks ago, but quickly learned that I was out of my depth with shaders. Instead, ilithya helped me write the Introduction to Shaders issue as a warm-up. Now it's race day!

If you're new to shaders and haven't read the Introduction to Shaders issue, that would be a good place to start. It provides a conceptual foundation that will make it easier to understand the shaders we're covering.

## Summer Screen

Let's dive deep into how ilithya made this awesome shader, Summer Screen.

There's a few things going on here. First, we've got these vertical line sections that are shifting back and forth. Then we've got these gradients coming in from the top and bottom like color waves on a beach. Let's figure out these two effects.

First we'll check out a shortened version of the GLSL code so we can see what's going on. You might see a new language and some math here but don't worry! We'll trot through them nice and slow.

``````// Create three static colors
vec3 colorA = vec3(0.5, 0., 0.5); // Purple
vec3 colorB = vec3(1.0, 0.41, 0.71); // Pink
vec3 colorC = vec3(0.0, 1.0, 1.0); // Teal blue

// Create three dynamic colors
vec3 color1 = mix(colorA, colorB, fract(x*fr+cos(u_time)));
vec3 color2 = mix(colorB, colorC, (y-sin(u_time))*0.5);
vec3 color3 = mix(color1, color2, x*cos(u_time+2.));

// Our color output
gl_FragColor = vec4(color3, 1.0);
``````

The first thing to address is that GLSL is a typed language, so when you declare a variable you also declare its type.

A `float` for our purposes can be thought of as a number with a decimal.

A `vec3` for our purposes is kind of like a special JavaScript array with three values.

A `vec4` has one more value than a `vec3` and is what the output of a shader `main()` function needs to be. The four values there align to red, green, blue, alpha.

First ilithya creates `colorA`, `colorB`, and `colorC`. Each of these are static colors that aren't going to change.

Then she uses these colors and other variables to create dynamic colors that will change. We'll look at the vertical lines first.

## Vertical Lines

For the vertical lines let's zoom in and see what's really going on here.

The lines are really repeated gradients going from dark to light.

To make gradients with GLSL we use the `mix` function. `mix` takes three arguments: `x`: the start of the range, `y`: the end of the range, and `a`: the point between `x` and `y`.

``````// vec3 mix(vec3 x, vec3 y, vec3 a)
vec3 color1 = mix(colorA, colorB, fract(x*fr+cos(u_time)));
``````

ilithya is making a gradient between the purple and pink, so those are her first two arguments. The colors get mixed in different amounts based on the value of `a`, which is a value from `0` to `1`. If `a` is `0`, we'll see 100% of the first color. If `a` is `1` we'll see 100% of the second color. Anything in between and we get a blend:

So that can give us one gradient, but how do we get repeating gradients?

To get the gradients to stop and start from `0` again, ilithya is using a GLSL function called `fract`. This returns the fraction of a number, dropping the integer. `fract(x)` is the same as doing `x - Math.floor(x)` or `x % 1` in JavaScript. So as `x` increases, `fract(x)` will always be a float between 0 and 1.

I'm going to simplify it beyond what ilithya has in her piece to show how the `fract` function works:

As we can see, even though `x` keeps increasing, the result of `fract(x)` just returns the fraction, or whatever comes after the decimal, giving us that same purplish color. Translate that over the entire piece and you've got ilithya's cool vertical bars!

We'll touch on how all these pieces animate a bit later.

Now we move to `color2`.

``````vec3 color2 = mix(colorB, colorC, (y - sin(u_time)) * 0.5);
``````

Another mix, this time between the pink and blue. No `fract` this time, so we're getting a smooth gradient. The `y` value makes it change color from bottom to top. We'll touch on the `sin(u_time)` bit soon.

## Putting it All Together

Then she combines `color1` and `color2`, using a mix amount that gets closer to `1` on the right side. The horizontal change is due to the `x` value. The `cos(u_time+2)` is for animation.

``````vec3 color3 = mix(color1, color2, x*cos(u_time+2.));
``````

Here's a simplified version of the shader's parts and the final color output:

You can see our simplified version looks similar to the finished shader, it's just missing animations.

## Animation

There are three main animations happening in Summer Screen:

1. The bars shifting left and right in `color1`
2. The pink and blue gradient moving up and down in `color2`
3. The mix value for `color3` moving left and right

Each of these are using the value of the current time, and familiar math functions, `sin` and `cos`.

``````vec3 color1 = mix(colorA, colorB, fract(x*fr+cos(u_time)));
vec3 color2 = mix(colorB, colorC, (y-sin(u_time))*0.5);
vec3 color3 = mix(color1, color2, x*cos(u_time+2.));
``````

That `time` value comes from a Three.js `Clock` and is how ilithya animates most of her shaders. It returns the elapsed time since the page loaded.

Don't worry if you don't know Three.js. The code ilithya uses is reusable boilerplate that makes the shader code simpler. We'd have to have a lot more GLSL code without the little bit of Three.js. It's a handy WebGL abstraction.

When ilithya calls either `sin(u_time)` or `cos(u_time)` she's going to get back a value between -1 and 1, and it'll transition smoothly. If you're familiar with CSS animations, it feels a lot like an `ease-in-out`.

Super Quick Sine Wave Refresher:

As we increase the value along the horizontal axis, the sine wave moves us up and peaks at `1`, brings us down to `0` and bottoms out at `-1` before climbing again. No matter high the horizontal value is, the sine of that value will always be between `-1` and `1`. Cosine works similarly but gives a different value.

And there you have it! Three different parts being blended and animated together to make one awesome animation.

## Break it Yourself

As she was helping me understand it, ilithya encouraged me to swap values out in the shader code. I highly recommend you go and do just that.

To see the vertical bars isolated, change the last line of the shader code to:

``````gl_FragColor = vec4(color1, 1.0);
``````

To see the vertical pink and blue gradient:

``````gl_FragColor = vec4(color2, 1.0);
``````

Or you can change that to `colorA`, `colorB`, or `colorC` to see a screen of solid color. Play with it, have fun, figure out how it works.

Check out Summer Screen →

## Bubble Gum

I won't be diving into how this shader works, but it's just so dang soothing to stare at, I had to share it. Let me know if you'd like to read about it in a future issue.

Check out Bubble Gum →