Gallery✨ Premium

WebGL Displacement Slideshow

A WebGL canvas slideshow: on every change, noise warps the outgoing and incoming images for 0.9 s. Your own images, pause on hover.

WebGLShaderSlideshow

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →

3 usage examples

Full-screen background for a fragrance collection — WebGL Displacement Slideshow example 1

① Full-screen background for a fragrance collection

WhenAt the top of Ombrevine's "Autumn collection" page, the slideshow covers the full width behind the menu, the fragrance name, its notes and the "Discover" button. Each bottle warps into the next; the name and notes change with the image. Arrows, dots and pause are grouped at the bottom right so they stay clear of the text.
WhyThe warp gives the move from one fragrance to the next a liquid texture that suits the product, where a plain crossfade would be ordinary. The dark gradient on the left keeps the text readable on any image.
SettingsRoot in position:absolute; inset:0, --wds-ratio: auto, square corners, four <img> (SVG illustrations), controls moved to the bottom right by the scene CSS, text in a layer above the canvas. The mouse stays over the background during the capture (so it's paused): the demo presses the real arrow every 1.7 s; the text follows the wds-on class set by the code.
A paint colour chart browsed with the dots — WebGL Displacement Slideshow example 2

② A paint colour chart browsed with the dots

WhenOn the Chromavel brand's colour page, the 250 px wide swatch shows a roller-painted wall; five dots under it move from "Raw Sienna" to "Storm Blue" or "Lichen Green", and the roller marks twist into the next shade while the name, reference, finish and the "Order a sample" button stay on the right.
WhyOn a perfectly flat colour the warp would not show: it needs some texture. A roller texture is enough, and the warp makes the change of shade feel physical, like stirred paint; nothing moves until the visitor chooses.
SettingsOne <img> per slide (an SVG roller texture, same origin), data-wds-colors kept as a fallback, data-wds-autoplay="false" and the pause button hidden since there is no autoplay, --wds-ratio: 250/186, white dots. In the capture the demo clicks the real dots every 1.5 s.
An online paper's "Featured" thumbnail — WebGL Displacement Slideshow example 3

③ An online paper's "Featured" thumbnail

WhenOn the Quotidelle home page, a small 140 × 82 px thumbnail at the top of the left column cycles through the week's three big reports, caption below; on the right, the article list by section. No arrows: at this size the dots and the pause button are enough.
WhySmall, the effect draws the eye to the Reports section without taking over the page: the warp lasts only 0.9 s and the thumbnail stays calm the rest of the time.
Settings--wds-ratio: 140/82, arrows hidden, 22 px pause button and 6 px dots (scene CSS), data-wds-interval="1600" for the capture (4000 to 6000 ms in real use). The caption follows the active slide.

How it works

The visuals are the .wds-slide elements written in the HTML: either an <img> (your photos) or two colours in data-wds-colors="#6d8bff,#5cffd8" (a diagonal gradient). On load the script copies each image into a WebGL texture, cropped like object-fit: cover to fill the 16/9 frame, and draws the first one on the .wds-canvas that covers the slides.

On every change, a shader reads value noise (a smoothed 4 × 4 grid) and uses it to shift both images in opposite directions, the old one more and more, the new one less and less, while they blend (smoothstep between 25% and 75% of the animation). The transition lasts 0.9 s, computed from elapsed time. The canvas follows its frame's size (ResizeObserver, pixel density capped at 2) and redraws immediately after a resize.

Autoplay advances every 3.5 s (data-wds-interval) and stops on mouse hover, while keyboard focus is inside, off screen, or with the pause button at the top right. ‹ › arrows, dots (one per slide, created by the script) and a horizontal swipe change slide. Without WebGL, with a cross-origin image served without CORS, or if the WebGL context is lost, the canvas steps aside and the HTML slides crossfade in 0.7 s.

Accessibility

  • Pause: a button named "Pause automatic playback" / "Resume automatic playback", plus a stop on hover, on keyboard focus and off screen. data-wds-autoplay="false" turns autoplay off (the button can then be hidden).
  • What gets read: the canvas is aria-hidden; the HTML slides are what screen readers read, with your <img> alt text. A aria-roledescription="carousel" region, role="group" slides "2 of 5", hidden ones inert.
  • Dots: real buttons named after their slide's aria-label; the current one carries aria-current="true". Arrows named "Previous" / "Next".
  • Reduced motion: under prefers-reduced-motion: reduce, no autoplay at start and no warping: the image changes at once. Everything stays controllable.
  • Touch: Pointer Events and touch-action: pan-y: a horizontal gesture changes image, the page scrolls vertically.

Browser compatibility

WebGL 1 (every browser since 2014), ResizeObserver, aspect-ratio and inert (Chrome 102, Firefox 112, Safari 15.5 according to MDN). Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): two slideshows with only the second one driven, 5 dots even with the script pasted twice, autoplay that advances without hover and stops on hover, image still drawn after a resize, real images in <img>, reduced motion, 390 px, finger swipe (Chromium), 0 errors. Crossfade fallback checked by forcing a WebGL context loss.

Chrome 102+✓ Full (measured)
Firefox 112+✓ Full (measured)
Safari 15.5+✓ Full (measured in WebKit)
Edge 102+✓ Full (Chromium engine)
Mobile iOS 15.5+✓ WebKit engine, not checked on a device
Android Chrome 102+✓ Full (touch measured)

Without WebGL or with a cross-origin image without CORS: CSS crossfade between the HTML slides, same buttons. Without JavaScript: the first slide stays visible (an image; a colour slide stays on the dark background).

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0801-root">
  <div class="wds-scene" data-wds data-wds-interval="3500" role="region" aria-roledescription="carousel" aria-label="…">
    <canvas class="wds-canvas" aria-hidden="true"></canvas>
    <div class="wds-slides" aria-live="off">
      <div class="wds-slide wds-on" role="group" aria-roledescription="slide" aria-label="1 of 5"><img src="…" alt="…"></div>
      <div class="wds-slide" role="group" aria-roledescription="slide" aria-label="2 of 5" data-wds-colors="#ff7eb6,#a777ff"></div>
      …
    </div>
    <button class="wds-btn wds-prev" type="button" aria-label="…" data-wds-prev>‹</button>
    <button class="wds-btn wds-next" type="button" aria-label="…" data-wds-next>›</button>
    <button class="wds-toggle" type="button" data-wds-toggle data-wds-label-pause="…" data-wds-label-play="…"><svg class="wds-icon-pause">…</svg><svg class="wds-icon-play">…</svg></button>
    <div class="wds-dots" data-wds-dots></div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Images (HTML — <img> in .wds-slide) none Your photos, cropped to fill the frame. Same domain as the page, or a server sending CORS headers (and crossorigin="anonymous" on the image); otherwise the slideshow falls back to a CSS crossfade.
Colour slide (HTML — data-wds-colors) "#6d8bff,#5cffd8" … (5 gradients) Two colours separated by a comma: a diagonal gradient, no image.
Interval (HTML — data-wds-interval on [data-wds]) 3500 (ms) Time between two automatic changes; 1500 ms minimum.
Autoplay (HTML — data-wds-autoplay="false") on (except reduced motion) Turns automatic advance off: only arrows, dots and swipe change the image.
Proportions (CSS — --wds-ratio) 16 / 9 Frame format; auto if you give it a height (full-screen background).
Pause button labels (HTML — data-wds-label-pause / data-wds-label-play) "Pause automatic playback" / "Resume automatic playback" Names read depending on the state; translate them.
Active dot (CSS — .wds-dot[aria-current="true"]) #5cffd8 Colour of the dot of the image shown.

FAQ

Browsers forbid reading a cross-origin image in WebGL without CORS permission. Host the images on the page's domain, or add crossorigin="anonymous" to the tag and the Access-Control-Allow-Origin header on the server. The crossfade is the planned fallback in that case.
As many as you like: one dot is created per slide. Each image becomes a texture in graphics memory (2048 px per side at most): beyond ten or so large photos, shrink them before publishing.
Not by itself: put your text inside each slide if you rely on the fallback, or watch the wds-on class change on the .wds-slide elements (a MutationObserver is enough) to update a title placed above.