I built the same thing 12 times and I'll do it again
Why do people hate repetition? I've spent years remaking the same thing over and over again - and I'm probably not stopping soon. Here's why remaking the same thing can teach you so much more than you might think.
Shuhari
It's always been fascinating to me the difference between how developers
and artists
approach repetition. Developers are generally encouraged to write DRY (Don't Repeat Yourself)
code, while artists are taught to iterate
, explore
, study
and experiment
within styles or techniques.
I've spent over 20 years in software, and I've seen time and time again that repetition is very often frowned upon, and even actively discouraged. PRs will be rejected if they contain duplicate code - some sort of lack of efficiency, or not understanding abstractions. The more senior the developer, the more likely they are to be critical of repetition.
Artists, on the other hand use repetition as a way to build mastery
, or gain a deeper level of understanding of the technique, style, or medium they're working with. The more experience an artist has, the more likely they are to actively use repetition to their advantage.
I really admire that approach - but maybe that's just my way of justifying the amount of time I've spent on this one technique!
I've spent years making and remaking Mesh Gradients. A dozen different versions, each one slightly different.
And honestly? I think it's starting to border on an unhealthy obsession
.
Why reinvent the wheel?
Like an artist would with a study, or a drawing exercise (gesture drawing, for instance), remaking and practicing
the same thing can really help you understand and internalise the technique.
Wih that in mind, every time I rebuilt my mesh gradient - even if the result didn't work out the way I wanted - I learned something new:
- Refined what I actually liked about these things
- Learned what was unnecessary complexity
- Researched new tools that made things easier to work with
- Explored different approaches I hadn't considered, or I was shaky with
One of the first ones I remember being really floored by was the wonderful Stripe website. I really loved the smooth, flowing shapes and patterns
they were using, and spent a lot of time studying it to try and figure out how they did it.
I built a few different versions of it, learning about interpolating between vertices (control points) and how to animate things in a vertex shader. The result I released on my generative art experiment Formation. I still really like this one. It might actually be the only one I was confident enough to release online.
Evolving
That version was all about the vertex shader
. From a technical perspective, it's probably the most accurate
mesh gradient I've made, but I did find it a little bit difficult to work with, so I wanted to try something different and focus on what I could do with a fragment shader
.
Just as an aside, the way I've approached things from here is not technically a mesh gradient
, it's more of a weighted color blend
. But I still think of it as a mesh gradient, because the results are still pretty similar.
Anyway, things went a little bit off the rails for a while. I started with experimenting with different ways of handling control points
and weights
.
Things ended up being overly complex. Too many moving parts. Even harder to control.
I started trying to use different coordinate systems
to create novel shapes and patterns - it kind of worked, but I still find it really difficult to understand what was going on.
Eventually, I decided to just start simplifying things. Focusing on techniques and patterns that I was very familiar with, and seeing how I could combine them to create something completely new.
My latest version was a lot simpler, but the results I'm starting to get with it are beautiful.
Here's what changed over time:
- Tools evolved - From GLSL to TSL (sometimes back again)
- Understanding deepened - I knew which techniques to layer
- Taste refined - I figured out what I actually wanted
- Code simplified - Less complexity, better output
Here's an example of the code I was writing for some of the older experiments. I haven't included all of the utility functions and uniforms I was using here, but you get the idea:
void main() {
vec2 uv = (v_uv * 2.0 - 1.0);
vec2 warped = warpPoint(uv);
// Convert to polar coordinates with smoothing
vec2 polar = cartesianToPolar(warped);
// Add some smoothing to the radius
polar.x = smoothstep(0.0, 1.0, polar.x * 0.5);
// Smooth the angle transition
polar.y = (polar.y + PI) / (2.0 * PI);
// Add some subtle variation
polar.x += sin(polar.y * (2.0 * PI)) * 0.1;
polar.y += cos(polar.x * (2.0 * PI)) * 0.1;
vec4 color = grad((polar + 1.0) / 2.0);
// Add subtle smoothing to final color
vec4 blurred = grad(polar + vec2(0.01, 0.01));
vec4 finalColor = mix(color, blurred, 0.3);
float weightPositions = 0.0;
float pointPositions = 0.0;
for (int i = 0; i < MAX_POINTS; i++) {
vec2 p = u_points[i];
vec2 w = u_weightVectors[i];
vec2 pointPos = uv - p;
float pS = max(abs(pointPos.x), abs(pointPos.y));
pS = 1.0 - step(0.01, pS);
pointPositions += pS;
vec2 weightPos = pointPos - (w / 10.0);
float pW = length(weightPos);//max(abs(weightPos.x), abs(weightPos.y));
pW = 1.0 - step(0.01, pW);
weightPositions += pW;
}
finalColor = mix(finalColor, vec4(vec3(0.0), 1.0), pointPositions);
finalColor = mix(finalColor, vec4(vec3(1.0, 0.0, 0.0), 1.0), weightPositions);
gl_FragColor = finalColor;
}
Compare this to parts of the TSL version I've been using for the latest version:
const finalColor = vec3(0).toVar()
const totalWeight = float(0).toVar()
// Loop through all colors and blend them together
Loop({ start: 0, end: colorsCount, type: 'float' }, ({ i }) => {
// Base angle for this color spot - creates different starting positions
const baseAngle = i.mul(PI)
// Calculate starting positions based on index, angle and speed
const x = sin(_time.mul(i.mul(0.75)).add(baseAngle))
const y = cos(_time.mul(2).add(baseAngle.mul(2.5)))
const pos = vec2(x, y)
const _c = colors.element(i)
// Calculate distance from current fragment to this color spot
const dist = length(uvR.sub(pos))
// Apply power function to create sharper falloff. This will create those sharper region edges that make mesh gradients so dramatic
dist.assign(pow(dist, 2.5))
// Calculate weight based on distance - closer spots have higher weight
const weight = div(1, max(0, dist))
// Accumulate color and total weight
finalColor.addAssign(_c.mul(weight.mul(weights.element(i))))
totalWeight.addAssign(weight)
})
// Normalize the color
finalColor.divAssign(totalWeight)
I think it's so much easier to read and understand! It's kind of crazy how much better it feels.
What keeps me coming back
Mesh gradients just hit different
for me. The shapes
, the textures
, the patterns
, the colors
- they just work.
But here's what really hooks me: Mesh gradients are a compound technique
.
They bring together different pieces:
- Noise functions (I really like using Turbulence)
- Color palettes (procedural gradients like Cosine Palettes)
- Distortion effects (warping the space like we do with Domain Repetition)
- Layering things all together
I think this is a really great example of how all the knowledge you build from all these different techniques can compound
to create something incredible.
When to move on vs when to iterate
Not every technique needs (or deserves) 12 versions. So how do you know?
Keep iterating when:
- You're still
discovering new things
- The results
keep surprising you
You enjoy the process
- this might be the most important one- Each version
teaches something new
Move on when:
You're bored
- never underestimate your gut feeling- Iterations
feel like busywork
- You've
extracted all the learning
Other techniques are calling your name
- this is supposed to be fun, chase the things that are exciting you
I have a folder of techniques and studies that I've reached a natural end-point with. Flame fractals
, voronoi fractals
, raymarching fractals
... Maybe I have a thing about fractals? I think I should revisit these things again!
Why this matters for your work
Whether you're into mesh gradients or not, I think this applies to any creative practice.
Maybe you're a shader artist
obsessed with fluid simulations. A design engineer
perfecting micro-interactions. A creative coder
refining particle systems.
Find your obsession
- the technique that pulls you back. The one you that keeps you up at night thinking about.
Then make, and remake it. Again and again. The real growth comes from revisiting
, refining
, obsessing
.
The Mesh series
It took a while to get here, but the Mesh Series is probably my favourite version yet. Not because it's the most complex thing I've ever made, but because I can really see how I've progressed and grown as a shader artist.
So yeah, I have an unhealthy obsession
with mesh gradients. I've made at least a dozen versions.
I'll probably make a dozen more.
Going further
Want to learn more about mesh gradients, or 9 other creative coding techniques? Unlock Fragments to get access to the full collection of 10 creative coding techniques, more than 15 utilities, and over 120 sketches with full breakdowns.