Effects and transitions
What the canvas does to the media while the slider moves, while the pointer is over it, and while one slide turns into the next. And how to write one.
Any number, in the order given.
| Effect | Does | Moves with |
|---|---|---|
stretch( { amount } ) | The middle of the image hangs back | Speed |
split( { amount } ) | The colours come apart | Speed |
parallax( { amount, zoom } ) | The image is slower than its slide; closer all round, so it keeps its shape | Position |
bend( { amount, speed } ) | The row is an arc; bends the mesh | Position and speed |
magnify( { size, strength } ) | A lens | Pointer |
cells( { size, zoom, reach } ) | Squares in the wake of the pointer, each its part closer; as far as the pointer is fast | Speed of the pointer |
spotlight( { size, dim } ) | The rest is dimmed | Pointer |
reveal( { size } ) | The images lose their colours, except around the pointer | Pointer |
glass( { size, lines, amount } ) | Fluted glass around the pointer | Pointer |
pixels( { size, cells } ) | Coarse pixels around the pointer | Pointer |
tilt( { angle } ) | The slide leans to the pointer; turns the mesh | Pointer |
waves( { size, amount, speed } ) | Rings run away from the pointer, while it is over the slider | Pointer and time |
smear( { size, amount } ) | The pointer drags the image along | Speed of the pointer |
shift( { size, amount } ) | The colours come apart along the way of the pointer | Speed of the pointer |
jelly( { amount, across } ) | The slides are soft: what moves them pulls them out of shape; bends the mesh | Speed |
slab( { edge, bend, spread, shine } ) | The slides are thick glass with a round bevel: the image is refracted through it and its colours come apart there, the rim catches a light at the top left, and a streak of reflection lies on the flat | Speed, a little |
Effects that lay out: the slider moves as ever, the effect says where a slide is drawn. For one slide per view or align: 'center'; give the slider padding for what leaves its place.
| Effect | Does |
|---|---|
coverflow( { angle, depth, range } ) | The slides beside the active one turn away |
pile( { offset, turn } ) | A pile of cards; the one on top leaves to the side |
fan( { angle, radius } ) | A hand of cards, or a wheel |
wave( { height, length, slope } ) | The row runs along a wave, across the view on a slope |
unweave( { amount, threads } ) | Near the edges of the view the slides come apart into threads |
dome( { amount, centre, size } ) | The slides as on a dome: what is far from the middle is smaller and nearer to it |
The page has such a slide in one place, and the canvas draws it in another. A click is a click on the slide that is seen: on the cover at the edge, not on the slide in whose place it is drawn. And the lightbox lets the image grow out of the slide as it is drawn: a cover that is turned away turns to the front while it grows, and turns back on its way home. canvas() sees to that with hit(), which it loads when an effect lays out; with a layer by its name it is a plugin to add:
import { gl, hit, coverflow } from 'gpuslider/gl';
const effects = [ coverflow() ];
createSlider( element, { align: 'center', plugins: [ gl( { effects } ), hit( { effects } ) ] } );
Effects under the pointer (waves(), spotlight(), magnify() and the others) have the pointer where it is on the slide as it is drawn.
slider.plugins.hit.at( x, y ) is the slide that is seen at a point of the page, .where( index ) is what is around a slide as it is drawn. What is there to be used in a slide (links, buttons) is where the page has it.
Transitions, for stack(), one at a time:
liquid, ripple, glitch, burn, pixelate, swirl, lens, fluted, warp, zoom, mosaic, blocks, fold, signal, push, chroma, kaleido, displace, datamosh, wind, distance. Without one the stack fades.
createSlider( element, {
plugins: [ stack(), canvas( { effects: [ split(), burn() ] } ) ],
} );
A page that lets its visitors choose names every transition it offers, with choose(): they are one shader, and pick( i ) changes the one that is drawn from the next move on, without a shader or a slider made again. A page that has one transition names that one, and has no other in its bundle.
import { choose, liquid, burn, push } from 'gpuslider/effects';
const turn = choose( [ liquid(), burn(), push() ] );
createSlider( element, { plugins: [ stack(), canvas( { effects: [ turn ] } ) ] } );
turn.pick( 1 ); // burn
A stack in the first view of a page says so in its HTML: class="gs gs-stack". Then the slides are on top of each other before the script is there, and nothing of the page gives way when it comes. Without the script such a slider shows its first slide, and is no scroller: the slides after the first cannot be reached then. It is for a stack that starts at its first slide.
Your own
An effect is an object of GLSL function bodies. It is written once and runs on both layers: for WebGPU it is translated to WGSL when the shader is made.
const wobble = ( { amount = 0.02 } = {} ) => ( {
params: { amount },
animated: true,
uv: `
uv.x += sin( uv.y * 20.0 + uTime * 3.0 ) * amount;
return uv;`,
} );
| Hook | Signature | |
|---|---|---|
vertex | vec3 ( vec3 p, vec2 uv ) | Moves the mesh. p in px from the centre of the slide, z towards the viewer |
uv | vec2 ( vec2 uv ) | Moves the lookup |
color | vec4 ( vec4 color, vec2 uv ) | Changes the result; may call media( uv ) |
transition | GLSL with vec4 transition( vec2 uv ) | Stack only. getFromColor, getToColor, progress: what is written for gl-transitions runs as it is |
A parameter is a number, or an array of 1 to 4. An array stays the array it was given: what the page writes into it is drawn in the next frame, which slider.wake() asks for. That is how a page tells an effect what only the page knows: where it is scrolled to, how loud the music is, where the middle of a wall of sliders is.
const level = [ 0 ];
const loud = () => ( {
params: { level },
uv: `return ( uv - 0.5 ) * ( 1.0 - 0.1 * level ) + 0.5;`,
} );
// Later, as often as it changes.
level[ 0 ] = 0.8;
slider.wake();
An effect that lays the slides out says in JS too where it moves a point to, so that a click finds the slide that is seen: place( p, about ) does what vertex does, with p as [ x, y, z ] and about with progress, velocity, size, view and quad as the uniforms of their names. Without it a click is a click on the slide whose place on the page it is.
head is GLSL the bodies need, animated: true says that it moves without the slider moving, animated: 'pointer' that it does while the pointer is over the slider.
Uniforms: uProgress, uVelocity, uPointer, uPointerSpeed, uPointerIn, uTime, uSize, uView, uQuad, and in color and uv also uRadius. See src/gl/program.js.
What the translation knows is the GLSL that all effects here are written in. For an effect that is to run on WebGPU too:
- Functions,
if,for,a ? b : c, and the functions GLSL comes with. Nooutandinoutparameters, no structs, no arrays, no#define. - What
max,min,clampandsmoothstepare given is of one kind:max( v, vec2( 0.0 ) ), notmax( v, 0.0 ). - One component is assigned at a time, or the whole vector:
p = vec3( q, p.z ), notp.xy = q.
A shader that WebGPU does not take is said in the console, as gpuslider: …, and the page draws that slider.







