Swept-strand gradient, drawn per pixel. Colour, form and shape pick independently — then tune. Presets come from ribbon-presets.json; retune here, paste back there.WebGL · no dependencies
Width
WebGL unavailable in this browser — the shader can't run here.
Colour
Form
Shape
Ratio
Scale
PNG
In situ
Desktop 21:9
Laptop 16:9
Tablet 4:3
Phone 3:4
measuring…≤8 noise samples per pixel · 2 compiled permutations
What the parameters do
Twist
Amplitude of the three sines that bend the centreline. At 0 the ribbon is a straight horizontal band.
Width
Thickness of the band, which also varies along x so the ribbon tapers.
Strands
Frequency of the striation term. It fades itself out wherever a full cycle would fall inside one pixel, so pushing it high thins the strands rather than tearing into moiré.
Striation
How much of that term reaches alpha. At 0 you get smooth colour bands; at 1, hairlines.
Glow
Two extra wide, soft passes behind the band. Reads as bloom on light grounds, as fog on dark.
Softness
Exponent on the edge falloff. Low values give a dense band with defined edges; high values dissolve it into haze.
Shape
Line keeps the centreline as a height field over x. Arc and circle feed the same code polar coordinates — angle for “along”, radius for “across” — with integer wave harmonics so the loop closes without a seam.
Opacity
Fades the ribbon back toward its own ground, so type or a logo can sit on top. On a transparent export it scales the alpha instead.
Zoom / Pan
Scale and offset on the sampling space, so the composition can be framed independently of the canvas. These travel with the config — they are part of the artwork, not the viewer.
Tilt
Rotates the sampling space, for a diagonal sweep across a hero.
How one pixel gets its colour
// signed distance from a curved centreline, normalised across the width
float c = centreOf(q.x, t, phase);
float w = (0.11 + 0.075*sin(q.x*1.5 + t*0.10 + phase)) * u_width;
float v = (q.y - c) / w; // -1 .. 1 inside the band
float m = 1.0 - v*v; // <= 0 means this pixel is outside
float vv = 0.5 + 0.5*v;
// vv is used twice: once for colour along the width, once at high
// frequency for the strands
float sh = 0.5 + 0.5*sin(vv*u_strands*PI + snoise(...)*2.6);
float ct = clamp(vv*0.46 + q.x*0.42 + 0.07*snoise(...), 0.0, 1.0);
vec3 col = ramp(ct); // linear-light mix
float a = pow(m, u_soft) * mix(1.0, 0.38 + 0.62*sh, u_striate);