Animation Basics
Using time for smooth oscillation
Static shaders are beautiful, but motion is what makes them mesmerizing. A pulsing glow, a drifting gradient, shapes that breathe and undulate. All of this comes from one simple ingredient: time.
The Time Uniform
Every frame, your shader receives a value called u_time. This is a float representing seconds since the shader started running. As time increases, everything in your shader can respond.
uniform float u_time;This single number is the heartbeat of animated shaders. Everything that moves, pulses, or cycles derives from it.
Interactive: A pulsing circle
The circle pulses using sin(u_time)
The code is simple. We use sin(u_time) to create a value that oscillates between -1 and 1. Map that to something visible, like the radius of a circle or the brightness of a color, and the shader comes alive.
The Sine Wave
The sine function is the workhorse of shader animation. It takes any input and returns a smoothly oscillating value between -1 and 1. When you feed it time, you get continuous, periodic motion.
Sine wave over time
Current value: 0.00
The wave completes one full cycle every seconds, which is about 6.28 seconds. That is slow. Usually we want more control over how fast things move.
Controlling Frequency
To speed up or slow down the oscillation, multiply time by a constant before passing it to sine.
float slow = sin(u_time * 0.5); // half speed
float normal = sin(u_time); // default
float fast = sin(u_time * 4.0); // 4x speedHigher multipliers mean faster oscillation. The value inside sin() is called the frequency. A frequency of 2 means two complete cycles per 6.28 seconds.
Interactive: Adjust the frequency
sin(u_time * 2.0)
Think of frequency as the tempo of your animation. A gentle pulse might use a frequency of 0.5, while a flickering strobe might use 10 or higher.
Controlling Phase
Sometimes you want multiple elements oscillating at the same speed but out of sync. This is where phase comes in. Adding a constant inside the sine function shifts the wave in time.
float a = sin(u_time);
float b = sin(u_time + 1.0); // shifted by 1 second
float c = sin(u_time + 3.14159); // shifted by half a cyclePhase offsets are how you create waves, ripples, and staggered animations. Each element starts at a different point in the cycle.
Interactive: Adjust the phase offset
Blue: sin(t) | Orange: sin(t + 0.0)
When the phase offset is , the two waves are perfectly opposite. When one is at its peak, the other is at its trough.
Ping-Pong Animation
The raw sine wave oscillates between -1 and 1. Often we want values between 0 and 1 instead. There are two common approaches.
The first remaps the range:
float value = sin(u_time) * 0.5 + 0.5; // now 0 to 1The second uses the absolute value, creating a "ping-pong" effect:
float value = abs(sin(u_time)); // 0 to 1 to 0 to 1...Comparing remapped sine vs ping-pong
Both range 0-1, but ping-pong bounces sharply at zero
The difference is subtle but important. The remapped sine spends equal time going up and down. The absolute value creates sharper bounces at the bottom since the wave reflects instead of wrapping.
Animating Position
To move a shape, add time-based offsets to your coordinates. A circle drifting horizontally:
vec2 center = vec2(sin(u_time) * 0.3, 0.0);
float d = length(uv - center) - 0.2;The circle moves left and right as sine oscillates. Multiply the sine by a smaller value to reduce the range of motion.
Interactive: Animated shape position
Different frequencies create Lissajous curves
Combining horizontal and vertical oscillations at different frequencies creates Lissajous patterns. Try frequencies that are slightly different for organic, drifting motion.
Animating Color
Color animation uses the same principles. Oscillate the RGB components independently for shifting hues, or oscillate a single brightness value for pulsing intensity.
vec3 color = vec3(
sin(u_time * 1.0) * 0.5 + 0.5,
sin(u_time * 1.3) * 0.5 + 0.5,
sin(u_time * 1.7) * 0.5 + 0.5
);Using slightly different frequencies for each channel creates smoothly shifting colors that never quite repeat.
Interactive: Color animation
RGB channels oscillate at slightly different frequencies
The key insight is that everything animatable, whether position, size, color, rotation, or distortion, can be driven by the same time-based functions. Once you understand how to shape a sine wave, you can animate anything.
Beyond Sine
While sine is the most common choice, cosine works identically (just shifted by ). You can also use triangle waves, sawtooth waves, or any periodic function. We will explore more sophisticated timing in the next chapter.
Key Takeaways
u_timeprovides seconds since shader start, driving all animationsin(u_time)oscillates smoothly between -1 and 1- Multiply time by a constant to control frequency (speed)
- Add a constant inside sin to control phase (offset)
- Use
abs(sin(t))for ping-pong orsin(t) * 0.5 + 0.5for 0-1 range - Different frequencies per axis create complex motion patterns
- These same techniques animate position, size, color, and any other property