Task 4 of 4
Gradients, cells and curves all thought in x and y. The last move of this module is to change coordinate systems inside the kernel: subtract the canvas center (63.5, 63.5 — halfway between the two middle rows and columns), and Pythagoras turns the thread's position into a radius. Anything you compute from that radius is automatically a perfect circle.
Feed the radius into a cosine and you get concentric ripples; multiply by a fade so
they die out toward the edge; tint the channels and the flat canvas turns into water.
One gotcha, handled in the starter: this.thread.x is an integer on
the GPU, so promote it to a float (this.thread.x / 1) before subtracting the
fractional center — otherwise the GPU rounds your 63.5 away.
One more thing comes prewired: a dial.
slider('phase', …) returns the value this run is using and puts a control
under the console; move it and the whole program re-runs. Phase slides the radius the
cosine sees — one radian of it is 1 / 0.35 px of radius — so at 0 the rings
stand still, and anywhere else the same rings sit further out. Drag it and the crests
travel; at 2π every crest has moved out by exactly one ring, which is the picture you
started from. The fade is computed before the slide, on the true radius, so the
vignette never moves — only the water does.
this.color(0.4v, 0.75v, v, 1).Math.sqrt(dx*dx + dy*dy) with dx, dy relative to (63.5, 63.5)wave = 0.5 + 0.5 * Math.cos(r * 0.35)fade = Math.max(0, 1 - r / 96), then v = wave * fade0.4*v, 0.75*v, vphase dial is prewired — drag it to send the rings travelling, but Run at its default 0, which is where these tests measureMath.cos(r * 0.35) swings between −1 and 1 as r grows —
0.5 + 0.5 * cos remaps that to 0…1, a crest roughly every 18 pixels.
const wave = 0.5 + 0.5 * Math.cos(r * 0.35);
const v = wave * fade;
this.color(0.4 * v, 0.75 * v, v, 1);
Use the fade the starter already computed rather than writing it again —
it was measured on the true radius, before the phase slide.
time uniform in an animated shader has ever done.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.