Pattern Repetition
This tutorial divides the canvas into an 8×8 grid, then draws a randomly rotated circle, square, or cross in each cell — cycling through the three types based on cell position:
Let's walk through the core mechanics.
Grid coordinates: floor and fract working together
floor(grid) gives integer cell IDs — which row, which column.
fract(grid) gives the local coordinate within a cell — a 0–1 range measured from the cell's bottom-left corner.
This is the standard template for tiled patterns: use floor to identify "which cell am I in?" and fract to draw inside each cell.
hash assigns a random value per cell
This is a common hash function — it maps the integer cell ID (x, y) to a pseudo-random value between 0 and 1. The same cell always returns the same value, but neighboring cells return very different ones.
This random value offsets each cell's rotation start angle, so they don't all spin in sync.
mod cycles through pattern types
Adding column and row IDs then taking mod 3 gives 0, 1, or 2 — used to cycle through circle, square, and cross patterns.
Try changing it
| Change | Effect |
|---|---|
Set gridSize from 8.0 to 4.0 | Larger cells, shapes are clearer |
Change 0.5 in rotation = u_time * 0.5 + ... to 2.0 | Rotation speeds up |
Change mod(..., 3.0) to mod(..., 2.0) | Only circles and squares, no crosses |
| Set all three colors to the same value | Different shapes, uniform color |
Exercise
In the exercise, use fract(uv * repeatCount) as the tiling coordinate, draw a cross pattern inside each cell, and finish with a dynamic blend between the cross and a circle shape.
Answer Breakdown
fract(uv * 5.0) tiles the canvas into a 5×5 grid. Subtracting vec2(0.5) re-centers each cell's coordinate at 0.
For the cross: step(-w, v) * step(v, w) returns 1 only within the -w to +w band. Taking max of the horizontal and vertical bands produces the cross shape.
For the circle: smoothstep(0.25, 0.2, dist) returns 1 when the distance is under 0.2 and 0 when over 0.25, giving a smooth circular edge.
mix(cross, circle, switchTime) switches between the two using a time-driven factor. switchTime oscillates with sin(u_time * 0.5).
Try setting repeatCount to 8.0 for a denser grid.