Shapes and Patterns
Learn to create various geometric shapes and complex patterns.
Basic Shapes
Circle
The most fundamental shape
Live Editor
const fragment = () => { // Distance from center const center = uv.sub(0.5) const distance = center.length() // Hard edge circle (aliased) const circle = step(distance, 0.3) return vec4(vec3(circle), 1) }
Anti-aliased Circle
Using fwidth()
for automatic edge smoothing:
Live Editor
const fragment = () => { // Distance from center const center = uv.sub(0.5) const radius = float(0.3) const distance = center.length() // Anti-aliasing using screen-space derivatives const edge = fwidth(distance) const circle = smoothstep(radius, radius.sub(edge), distance) return vec4(vec3(circle), 1) }
MSAA Circle
Using multi-sampling for superior edge quality:
Live Editor
const fragment = () => { const msaa = Fn(([uv, AA]) => { const center = uv.sub(0.5) const ret = float(0).toVar() Loop(int(AA), ({ i: m }) => { Loop(int(AA), ({ i: n }) => { const offset = vec2(float(m), float(n)) const diff = offset.div(AA).sub(0.5).div(iResolution) const distance = center.add(diff).length() const circle = step(distance, 0.3) const value = ret.add(circle) ret.assign(value) }) }) const divisor = AA.mul(AA) return ret.div(divisor) }) return vec4(vec3(msaa(uv, 4)), 1) }
Polygons
Triangle
Live Editor
const fragment = () => { // Triangle using polygon generator (keep original function) const polygon = Fn(([xy, sides, size]) => { const angle = atan2(xy.y, xy.x) const radius = xy.length() const pi2 = 6.28318 const a = angle.div(pi2).mul(sides) const r = a.floor().sub(a).add(0.5).mul(pi2).div(sides).cos() const distance = radius.mul(r) // AA const edge = fwidth(distance) return smoothstep(size, size.sub(edge), distance) }) const center = uv.sub(0.5) const triangle = polygon(center, 3, 0.2) return vec4(vec3(triangle), 1) }
Regular Polygon
Create any regular polygon:
Live Editor
const fragment = () => { // Function to create polygon const center = uv.sub(0.5) const gap = 0.25 const polygon = Fn(([xy, sides, size]) => { const angle = atan2(xy.y, xy.x) const radius = xy.length() const pi2 = 6.28318 const a = angle.div(pi2).mul(sides) const r = a.floor().sub(a).add(0.5).mul(pi2).div(sides).cos() const distance = radius.mul(r) // AA const edge = fwidth(distance) return smoothstep(size, size.sub(edge), distance) }) // Create different polygons with consistent visual size const shapes = polygon(vec2(-gap, gap).add(center), 3, 0.1) .add( polygon(vec2( gap, gap).add(center), 4, 0.14) ) .add( polygon(vec2(-gap, -gap).add(center), 6, 0.15) ) .add( polygon(vec2( gap, -gap).add(center), 8, 0.16) ) return vec4(vec3(shapes), 1) }
Star Shape
Live Editor
const fragment = () => { // Pentagram (5-pointed star) using distance field const center = uv.sub(0.5) const edge1 = vec2(0.809016994, -0.587785252).constant() const edge2 = vec2(edge1.x.negate(), edge1.y).constant() const edge3 = vec2(0.309016994, -0.951056516).constant() const sdPentagram = Fn(([p, radius]) => { let point = vec2(p.x.abs(), p.y) // First edge reflection const distance1 = max(point.dot(edge1), 0) const offset1 = edge1.mul(distance1).mul(2) point = point.sub(offset1) // Second edge reflection const distance2 = max(point.dot(edge2), 0) const offset2 = edge2.mul(distance2).mul(2) point = point.sub(offset2) // Final adjustments point = vec2(point.x.abs(), point.y.sub(radius)) const clampValue = clamp(point.dot(edge3), 0, 0.726542528) const finalOffset = edge3.mul(clampValue) return point.sub(finalOffset).length() }) // Create star const starDist = sdPentagram(center, 0.2) const starShape = smoothstep(0.02, 0, starDist) return vec4(vec3(starShape), 1) }
Lines and Curves
Straight Lines
Live Editor
const fragment = () => { // Horizontal line const absY = uv.y.abs() let c = smoothstep(0.02, 0, absY) // Vertical line const absX = uv.x.abs() c = c.add(smoothstep(0.02, 0, absX)) // Diagonal line const diagDiff = uv.y.sub(uv.x).abs() c = c.add(smoothstep(0.02, 0, diagDiff)) return vec4(vec3(c), 1) }
Curves
Live Editor
const fragment = () => { // Sine wave const center = uv.sub(0.5).mul(2) const sineWave = center.x.mul(4).sin().mul(0.3) const sineDistance = center.y.sub(sineWave).abs() const sineLine = smoothstep(0.05, 0, sineDistance) // Parabola const parabola = pow(center.x, 3) const parabolaDistance = center.y.sub(parabola).abs() const parabolaLine = smoothstep(0.05, 0, parabolaDistance) // Circle outline const circleDistance = center.length().sub(0.5).abs() const circleLine = smoothstep(0.05, 0, circleDistance) const maxParabolaCircle = max(parabolaLine, circleLine) const curves = max(sineLine, maxParabolaCircle) return vec4(vec3(curves), 1) }
Complex Patterns
Fractal-like Pattern
Live Editor
const fragment = () => { // Create fractal pattern using Loop with configurable iterations const generateFractal = Fn(([uv, N]) => { const ret = vec3(0).toVar() // Multiple scales of the same pattern Loop(int(N), ({ i }) => { const layerIndex = float(i) const scale = pow(2, layerIndex) const scaledPos = uv.mul(scale) // Circle pattern at each scale const dist = scaledPos.fract().sub(0.5).length() const circle = smoothstep(0.3, 0.2, dist) // Add to final color with decreasing weight const layerRatio = layerIndex.div(float(N)) const weight = layerRatio.oneMinus() const circleVec = vec3(circle).mul(weight) ret.addAssign(circleVec) }) const divisor = float(N) return ret.div(divisor) // Normalize }) const fractalColor = generateFractal(uv, 8) return vec4(fractalColor, 1) }
Voronoi Pattern
Live Editor
const fragment = () => { const st = uv.mul(5) const row = st.floor() const col = st.fract() const random2 = Fn(([p]) => { const dot1 = vec2(127.1, 311.7).dot(p) const dot2 = vec2(269.5, 183.3).dot(p) return vec2(dot1, dot2).sin().mul(43758.5453).fract() }) const voronoi = Fn(([gridRow, gridCol]) => { const minDist = float(1).toVar('minDist') const closestPoint = vec2(0).toVar('closestPoint') Loop(int(9), ({ i }) => { const idx = i.toFloat() const x = mod(idx, 3).floor().sub(1) const y = idx.div(3).floor().sub(1) const offset = vec2(x, y).toVar() const gridPoint = gridRow.add(offset) const point = random2(gridPoint).toVar() const dist = offset.add(point).sub(gridCol).length().toVar() const closer = step(dist, minDist).toVar() const newMinDist = mix(minDist, dist, closer) minDist.assign(newMinDist) const newClosestPoint = mix(closestPoint, point, closer) closestPoint.assign(newClosestPoint) }) return closestPoint }) const result = voronoi(row, col) return vec4(vec3(result, 0.8), 1) }
Dynamic Patterns
Game of Life
Live Editor
const fragment = () => { const hash = Fn(([coord]) => { const dotResult = vec2(127.1, 311.7).dot(coord) return dotResult.sin().mul(43758.5453).fract() }) const automata = Fn(([coord, time]) => { let neighbors = float(0).toVar('neighbors') Loop(int(9), ({ i }) => { const idx = float(i) const x = mod(idx, 3).floor() const y = idx.div(3).floor() const offset = vec2(x, y).toVar() If(offset.length().greaterThan(0.1), () => { const coordOffset = coord.add(offset) const cell = step(0.5, hash(coordOffset)) neighbors.addAssign(cell) }) }) const coordTime = coord.add(time) const alive = step(0.5, hash(coordTime)) const birthRange = step(2.5, neighbors).mul(step(neighbors, 3.5)) const birth = alive.oneMinus().mul(birthRange) const survivalRange = step(1.5, neighbors).mul(step(neighbors, 3.5)) const survival = survivalRange.mul(alive) return max(birth, survival) }) const p = uv.mul(64) const local = p.fract() const t = iTime.floor() const life = automata(p.floor(), t) const color = local.length() const lifeColor = color.mul(life) return vec4(vec3(lifeColor), 1) }
Morphing Patterns
Live Editor
const fragment = () => { // Morphing between different patterns const t = iTime.sin() // Pattern A: Circles const center = uv.sub(0.5) const circles = center.length().mul(10).sin() // Pattern B: Stripes const stripes = uv.x.mul(10).sin() // Pattern C: Grid const gridPattern = uv.mul(10).sin() const grid = gridPattern.x.mul(gridPattern.y) // Morph between patterns const mixRatio1 = smoothstep(0, 0.33, t) let pattern = mix(circles, stripes, mixRatio1) const mixRatio2 = smoothstep(0.33, 0.66, t) pattern = mix(pattern, grid, mixRatio2) const mixRatio3 = smoothstep(0.66, 1, t) pattern = mix(pattern, circles, mixRatio3) pattern = pattern const color = vec3(1, 0.8, 1.2).mul(pattern) return vec4(color, 1) }
What You've Learned
- ✅ Basic geometric shapes (circles, polygons, stars)
- ✅ Line and curve drawing techniques
- ✅ Grid and tiling patterns
- ✅ Complex pattern generation (fractals, voronoi, moire)
- ✅ Dynamic and animated patterns
Next Steps
Now that you can create shapes and patterns, learn about Images and Textures to work with external images and create complex surface effects.