Randomised block dissolve
Each rectangular block is assigned a random dissolve threshold. During the transition, blocks flip from the outgoing slide to the incoming one when progress crosses their threshold — producing a stochastic, pixel-art-style dissolve. blockSize controls the granularity: 8–16 px reads as organic noise, 32 px is clearly blocky, 64 px gives a retro 8-bit look. A longer speed (900–1200 ms) lets the full pattern develop.
View code
<div class="wgl-slider" id="my-slider">
<div class="c--slider-a__wrapper">
<!-- Slides are empty — WebGL renders the images -->
<div class="wgl-slide" data-slide></div>
<div class="wgl-slide" data-slide></div>
<div class="wgl-slide" data-slide></div>
<div class="wgl-slide" data-slide></div>
</div>
</div>
<div id="my-pag" class="c--slider-a__pagination"></div> .wgl-slider { position: relative; height: 400px; overflow: hidden; background: #000; }
.wgl-slider .c--slider-a__wrapper { display: flex; }
.wgl-slide { flex-shrink: 0; width: 100%; height: 100%; }
.wgl-slider canvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; } .wgl-slider { position: relative; height: 400px; overflow: hidden; background: #000; }
.wgl-slider .c--slider-a__wrapper { display: flex; }
.wgl-slide { flex-shrink: 0; width: 100%; height: 100%; }
.wgl-slider canvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; } import { Slider } from '@andresclua/sliderkit'
import { arrows, pagination } from '@andresclua/sliderkit-plugins'
const IMAGES = [
'https://picsum.photos/seed/px1/900/400',
'https://picsum.photos/seed/px2/900/400',
'https://picsum.photos/seed/px3/900/400',
'https://picsum.photos/seed/px4/900/400',
]
const VERT = `
attribute vec2 a_pos; attribute vec2 a_uv; varying vec2 vUv;
void main() { vUv = a_uv; gl_Position = vec4(a_pos, 0.0, 1.0); }
`
const FRAG = `
precision highp float;
uniform sampler2D uFrom; uniform sampler2D uTo;
uniform float uProgress;
uniform vec2 uBlocks; // (canvasW / blockPx, canvasH / blockPx)
varying vec2 vUv;
float rand(vec2 n) {
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
void main() {
vec2 blockId = floor(vUv * uBlocks);
float thresh = rand(blockId);
float amt = step(thresh, uProgress);
gl_FragColor = mix(texture2D(uFrom, vUv), texture2D(uTo, vUv), amt);
}
`
// blockSize in CSS pixels
const BLOCK = 32
const container = document.querySelector('#my-slider')
const canvas = document.createElement('canvas')
container.insertBefore(canvas, container.firstChild)
const gl = canvas.getContext('webgl')
let blocksX = 1, blocksY = 1
function resize() {
const w = container.offsetWidth, h = container.offsetHeight
canvas.width = w * Math.min(devicePixelRatio, 2)
canvas.height = h * Math.min(devicePixelRatio, 2)
canvas.style.width = w + 'px'; canvas.style.height = h + 'px'
gl.viewport(0, 0, canvas.width, canvas.height)
blocksX = canvas.width / BLOCK
blocksY = canvas.height / BLOCK
}
resize(); window.addEventListener('resize', resize)
// ... compile prog, create quad buffers, loadTex same as displacement demo ...
Promise.all(IMAGES.map(loadTex)).then(textures => {
draw(textures[0], textures[0], 0)
const slider = new Slider('#my-slider', {
loop: true, speed: 1,
plugins: [arrows(), pagination({ el: '#my-pag', type: 'dots', clickable: true })],
})
slider.on('afterSlideChange', ({ index, previousIndex }) => {
animateTransition(textures[previousIndex], textures[index], 900)
})
}) Chunky 8-bit dissolve
The same shader with blockSize set to 56 px, producing large retro blocks. Combining a longer speed (1100 ms) with big blocks lets each one pop visibly — the effect reads as deliberate, almost game-over-screen pixelation rather than noise.
View code
<div class="wgl-slider" id="my-slider">
<div class="c--slider-a__wrapper">
<div class="wgl-slide" data-slide></div>
<div class="wgl-slide" data-slide></div>
<div class="wgl-slide" data-slide></div>
</div>
</div>
<div id="my-pag" class="c--slider-a__pagination"></div> .wgl-slider { position: relative; height: 400px; overflow: hidden; background: #000; }
.wgl-slider .c--slider-a__wrapper { display: flex; }
.wgl-slide { flex-shrink: 0; width: 100%; height: 100%; }
.wgl-slider canvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; } .wgl-slider { position: relative; height: 400px; overflow: hidden; background: #000; }
.wgl-slider .c--slider-a__wrapper { display: flex; }
.wgl-slide { flex-shrink: 0; width: 100%; height: 100%; }
.wgl-slider canvas { position: absolute; inset: 0; z-index: 1; pointer-events: none; } // Same setup — only blockSize changes
const BLOCK = 56 // large blocks → retro 8-bit look
new Slider('#my-slider', {
loop: true, speed: 1,
plugins: [arrows(), pagination({ el: '#my-pag', type: 'dots', clickable: true })],
})