So far, almost everything we've created has been based on circles.
Circles are useful, but many user interfaces, games, and procedural graphics rely on rectangles.
Buttons.
Windows.
Panels.
Progress bars.
Health bars.
Cards.
Menus.
All of these begin with a simple rectangle.
Unlike traditional graphics, we won't import an image.
We'll generate the rectangle mathematically.
A Rectangle Is Just Boundaries
Think about a sheet of paper.
A rectangle isn't defined by a center like a circle.
Instead, it is defined by four edges.
- Left
- Right
- Top
- Bottom
Every pixel asks a simple question.
"Am I inside all four edges?"
If the answer is yes, the pixel belongs to the rectangle.
Starting with UV Coordinates
As always, we'll use UV coordinates.
vec2 uv = vUv;
Remember that both coordinates range from 0.0 to 1.0.
This makes it easy to describe positions on the screen.
Creating the Horizontal Bounds
Let's create the left and right edges.
float left = step(0.30, uv.x);
float right = 1.0 - step(0.70, uv.x);
Pixels between these values become white.
Everything else becomes black.
Creating the Vertical Bounds
Now do the same for the vertical direction.
float bottom = step(0.30, uv.y);
float top = 1.0 - step(0.70, uv.y);
Again, only pixels inside these limits remain.
Combining All Four Edges
To create the rectangle, multiply the four results.
float rect =
left *
right *
bottom *
top;
Only pixels that satisfy every condition remain visible.
This creates a perfect rectangle.
Complete Shader
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 vUv;
void main(){
vec2 uv = vUv;
float left = step(0.30, uv.x);
float right = 1.0 - step(0.70, uv.x);
float bottom = step(0.30, uv.y);
float top = 1.0 - step(0.70, uv.y);
float rect = left * right * bottom * top;
gl_FragColor = vec4(vec3(rect), 1.0);
}
There are no textures.
No imported images.
Every pixel simply decides whether it belongs inside the rectangle.
Changing the Size
Move the edges farther apart.
left = step(0.20, uv.x);
right = 1.0 - step(0.80, uv.x);
The rectangle becomes wider.
Move the top and bottom edges.
bottom = step(0.15, uv.y);
top = 1.0 - step(0.85, uv.y);
Now it becomes taller.
Creating a Square
Use equal spacing on both axes.
0.35
0.65
The result is a square.
Because a square is simply a rectangle with equal sides.
Softening the Edges
Replace step() with smoothstep().
float left = smoothstep(0.29, 0.31, uv.x);
float right = 1.0 - smoothstep(0.69, 0.71, uv.x);
Repeat this for the remaining edges.
The rectangle now has smooth edges instead of sharp ones.
This is useful for modern user interfaces.
Adding Colour
Multiply the rectangle by any colour.
vec3 color = vec3(0.2, 0.8, 1.0) * rect;
gl_FragColor = vec4(color, 1.0);
Now only the rectangle is coloured.
Animating the Rectangle
Let's make the rectangle grow.
float size = 0.20 + sin(uTime) * 0.05;
Now use that value for the edges.
left = step(0.5 - size, uv.x);
right = 1.0 - step(0.5 + size, uv.x);
Do the same for the vertical edges.
The rectangle now expands and shrinks smoothly.
Combining Shapes
Remember the previous lesson.
Shapes are just numbers.
That means we can combine a rectangle with a circle.
float shape = rect + circle;
Or subtract one.
float shape = rect - circle;
Procedural graphics become much more interesting when different primitives work together.
Where Are Procedural Rectangles Used?
Rectangles appear almost everywhere.
Some common examples include
- Buttons
- Windows
- Health bars
- Menus
- Cards
- Tooltips
- Dialog boxes
- User interface panels
- Pixel art
- HUD elements
Learning rectangles is one of the biggest steps toward creating complete procedural interfaces.
Try These Experiments
Create a thin rectangle.
Increase its width.
Turn it into a square.
Animate its size using sin().
Combine it with a circle.
Subtract a circle from the middle.
Each experiment builds your understanding of procedural construction.
A Small Challenge
Can you create these effects?
- A simple button.
- A centered square.
- A glowing panel.
- A rectangle with rounded corners by combining it with circles.
- A pulsing user interface element.
Try solving them before moving on.
You'll discover that almost every procedural interface starts with these same building blocks.
What We Learned
Today we learned how to create rectangles using only UV coordinates and the step() function.
Instead of measuring distance from a point, we defined four boundaries and checked whether each pixel stayed inside them.
Together with circles, rectangles become one of the most important procedural primitives you'll ever use.