Until now, every shader we have created has been completely static.
A gradient stays in one place.
A stripe never moves.
A repeated pattern looks exactly the same every frame.
That changes today.
In this tutorial, we are going to introduce time into our shaders. Once a shader knows how much time has passed, it can animate colors, move patterns, create waves, and produce effects that feel alive.
Animation is simply a value that changes every frame.
This tutorial builds on everything we have learned so far.
Part 1: Your First Shader: Painting the Entire Screen One Color
Part 2: Understanding UV Coordinates: The Secret Behind Every Shader
Part 3: Your First Gradient: Using UV Coordinates to Paint Space
Part 4: Mixing Colors with mix(): Creating Smooth Color Transitions
Part 5: Keeping Values Under Control with clamp()
Part 6: Adding Contrast with pow(): Making Gradients More Interesting
Part 7: Repeating Patterns with fract(): Creating Infinite Tiles
Part 8: Drawing Sharp and Smooth Edges with step() and `smoothstep()
Now we are ready to make our shaders move.
A shader runs many times every second.
If nothing changes between frames, the image stays the same.
But if one value changes continuously, the shader produces animation.
That changing value is usually called uTime.
The u stands for uniform, which means the value is sent to the shader from the program that is running it.
Unlike vUv, which changes for every pixel, uTime has the same value everywhere on the screen.
It simply increases as time passes.
Before using time, we need to declare it.
uniform float uTime;
Think of uTime as a stopwatch.
When the shader first starts, it might be
0.0
One second later it becomes
1.0
After two seconds
2.0
It continues increasing for as long as the shader is running.
Let's display the value directly.
#ifdef GL_ES
precision mediump float;
#endif
uniform float uTime;
void main() {
gl_FragColor = vec4(vec3(uTime), 1.0);
}
This will quickly become completely white because uTime keeps increasing.
The value never resets.
That is why we usually combine time with other functions.
Instead of displaying time directly, add it to the UV coordinates.
float value = vUv.x + uTime;
As uTime increases, the gradient slides across the screen.
Every frame shifts the gradient a little farther.
Sometimes the animation is too fast.
Multiply the time value.
float value = vUv.x + uTime * 0.2;
Now the movement is much slower.
If you want it faster, try
float value = vUv.x + uTime * 2.0;
The number acts like a speed control.
Time can also control color blending.
vec3 color = mix(
vec3(0.0, 0.4, 1.0),
vec3(1.0, 0.2, 0.6),
fract(uTime)
);
gl_FragColor = vec4(color, 1.0);
Here the blend value keeps restarting because fract() removes the whole number.
The colors continuously blend from one to the other.
Let's combine what we learned about fract() with time.
float stripes = fract(vUv.x * 8.0 + uTime);
gl_FragColor = vec4(vec3(stripes), 1.0);
Instead of standing still, the repeating pattern now scrolls across the screen.
Adding one changing value creates movement without changing the rest of the shader.