Drawing Sharp and Smooth Edges with `step()` and `smoothstep()`
In the previous tutorial, we learned how to repeat patterns using fract(). By restarting UV coordinates over and over, we created stripes, tiles, and repeating gradients.
Those patterns were still smooth because gradients change continuously.
Sometimes, however, you do not want a smooth transition.
You may want a perfectly sharp line.
Or perhaps you want a line that fades gently instead of changing instantly.
That is exactly what step() and smoothstep() are designed to do.
These two functions are used everywhere in GLSL. They are the building blocks for lines, borders, circles, masks, and many procedural shapes.
Before You Continue
This tutorial builds on the previous lessons.
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
Now we are ready to turn smooth gradients into clean shapes.

What Is step()?
The step() function creates an instant change between two values.
Its structure is
step(edge, value)
If the value is smaller than the edge, the result is 0.0.
If the value is greater than or equal to the edge, the result is 1.0.
That is all it does.
A Simple Example
Suppose we write
step(0.5, vUv.x)
Everything to the left of the middle of the screen becomes
0.0
Everything to the right becomes
1.0
Instead of a smooth gradient, we now have a sharp dividing line.
Drawing a Vertical Line
Here is a complete shader.
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 vUv;
void main() {
float line = step(0.5, vUv.x);
gl_FragColor = vec4(vec3(line), 1.0);
}
The screen is divided exactly in half.
One side is black.
The other side is white.
There is no gradual transition.
Changing the Position
The edge does not have to stay in the middle.
Move it closer to the left.
step(0.2, vUv.x)
Or move it toward the right.
step(0.8, vUv.x)
Changing the first number changes where the line appears.
What Is smoothstep()?
Sometimes a perfectly sharp edge looks too harsh.
That is where smoothstep() comes in.
Its structure is
smoothstep(start, end, value)
Instead of changing instantly, it creates a smooth transition between two values.
A Simple Example
smoothstep(0.4, 0.6, vUv.x)
Instead of an instant jump at the center, the color slowly changes between 0.4 and 0.6.
Everything before 0.4 stays black.
Everything after 0.6 becomes white.
Only the area between them fades smoothly.
Comparing the Two
Using step().
████████░░░░░░░░
Using smoothstep().
███████▓▒▒░░░░░░
Both functions divide the screen.
The difference is how they handle the edge.
Creating a Soft Gradient Edge
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 vUv;
void main() {
float value = smoothstep(0.3, 0.7, vUv.x);
gl_FragColor = vec4(vec3(value), 1.0);
}
Instead of a hard cut, the brightness slowly changes across the center of the screen.
Using step() with fract()
These two functions work very well together.
float stripe = step(0.5, fract(vUv.x * 8.0));
gl_FragColor = vec4(vec3(stripe), 1.0);
Instead of repeating gradients, you now get repeating black and white stripes.
This is one of the simplest procedural patterns you can create.
Using smoothstep() with fract()
Now replace step().
float stripe = smoothstep(0.4, 0.6, fract(vUv.x * 8.0));
gl_FragColor = vec4(vec3(stripe), 1.0);
The stripes now have soft edges instead of perfectly sharp ones.
This small change makes the pattern look much smoother.
Why Are These Functions Important?
You will find step() and smoothstep() in shaders that create
- Lines
- Borders
- Circles
- Masks
- Progress bars
- User interface elements
- Glow effects
- Soft shadows
- Procedural textures
Almost every shader artist uses them.
Try These Experiments
Draw a line in the middle.
step(0.5, vUv.x)
Move the line.
step(0.25, vUv.x)
Create a soft edge.
smoothstep(0.3, 0.7, vUv.x)
Create repeating stripes.
step(0.5, fract(vUv.x * 10.0))
Try changing the numbers and observe how the result changes.
A Small Challenge
Can you create these effects?
- Five sharp vertical stripes.
- Five soft vertical stripes.
- A line across the center of the screen.
- A soft band that fades from black to white.
- Thick stripes and thin stripes by changing the repetition value.
Spend some time experimenting before moving on.
These two functions become much easier to understand once you see them in action.
Posted Using INLEO