How it works
01 Two grades from one texture
There is only one image loaded, a single sampler2D. Grade A is the plain sampled colour. Grade B is a duotone derived from the same sample: each pixel's luminance is remapped to a two-colour palette (deep aubergine in the shadows, warm cream in the highlights). The grades live entirely in the fragment shader; no second asset, no second draw call.
02 Noise-displaced wipe edge
The wipe progresses left to right via u_progress (0 = A, 1 = B). The
transition edge is not a straight vertical line. It is displaced by a four-octave
fractional Brownian motion field. Each pixel computes its own threshold
(uv.x + fbm(uv)), so the boundary tears and flows organically. A smoothstep window on either side of the edge gives a soft blend zone.
A thin bright seam sits exactly at the wipe front and carries an animated shimmer
that pulses along its length, then fades as the transition completes.
// One texture, two grades ─────────────────────────────────
vec3 colorA = texture2D(u_image, uv).rgb; // plain sample
vec3 colorB = duotone(colorA); // aubergine → cream
// Noise-displaced wipe edge (4-octave FBM on the diagonal)
float disp = fbm(uv * 3.5) * 0.35;
float edge = uv.x + disp;
float reveal = smoothstep(edge - 0.025, edge + 0.025, u_progress);
// Thin seam with an animated shimmer — disappears at 0 and 1
float seam = smoothstep(0.04, 0.0, abs(u_progress - edge))
* smoothstep(0.01, 0.06, u_progress)
* smoothstep(0.99, 0.94, u_progress);
vec3 col = mix(colorA, colorB, reveal);
col = mix(col, vec3(1.0, 0.97, 0.88), seam * 0.90); 03 Eased toggle
Clicking captures the current interpolated position as the new start, flips the
target, and records a timestamp. Each RAF frame calls an ease-in-out cubic to
interpolate from start to target over 0.9 seconds, so you can click mid-transition
and it reverses smoothly from wherever it is. Under prefers-reduced-motion the jump is instant; the shader draws once and
the RAF loop is never started.
A self-contained Svelte action, <canvas use:wipe>. One texture,
one pass, FBM-displaced edge. Open to borrow.