A for loop that repeatedly scales, tiles (fract), and accumulates color creates multi-scale fractal detail in shaders
Wrapping the core UV transformation and color calculation inside a for loop, where each iteration multiplies the UV scale (e.g., by 1.5 or 2.0) and applies fract, produces self-similar detail at progressively smaller scales — a procedural fractal. Each iteration works on a tiled, re-centered version of the UV coordinates and adds its contribution to a running color total. To avoid visual monotony where tiles align perfectly (caused by integer multipliers), non-integer scaling (e.g., ×1.5) breaks the symmetry. Tracking the pre-iteration UV separately (as uv0) lets you feed the original global distance to the palette function for consistent large-scale coloring while the local tiled UVs produce the fine detail.
Examples
vec3 finalColor = vec3(0.0);
vec2 uv0 = uv; // save original UV
for (float i = 0.0; i < 4.0; i++) {
uv = fract(uv * 1.5) - 0.5; // tile with non-integer scale
float d = length(uv) * exp(-length(uv0));
finalColor += palette(length(uv0) + i * 0.4 + iTime * 0.4, ...) / d;
}
fragColor = vec4(pow(finalColor, vec3(1.2)), 1.0);
Assessment
Explain why multiplying UV by exactly 2 each iteration creates less visual interest than multiplying by 1.5. Predict qualitatively what happens to the fractal when you increase from 3 to 6 iterations.