Image

A reveal for <img>, also inside <picture>. Along the receding ink the picture itself bleeds through: the effect samples the image as a texture. The image is never altered.

Live example: press Replay
Plate No. 1: a black disc with a red center, and an ink stroke

Usage

<figure class="stage">
  <img src="photo.jpg" alt="…" loading="lazy" width="1200" height="800">
</figure>
.stage img { display: block; width: 100%; height: auto; }
/* While the image loads, keep it invisible (opacity: it stays in the accessibility tree). */
[data-fx-pending] img { opacity: 0; }
import { createRenderer, createImageFx } from 'webgl-ui'

const renderer = createRenderer()
document.querySelectorAll('.stage img').forEach(img => createImageFx(img, { renderer }))

The image's parent is the stage the canvas covers: it should wrap the image tightly. The reveal plays when the image loads (lazy loading included); an image that is already loaded is left alone unless you call reveal().

Options

OptionTypeDefaultDescription
rendererRenderer | () => RendererrequiredThe shared renderer.
effectEffectDefinkImageEffectAn image effect (samples uImage) or any cover effect.
durationnumber1100Reveal duration in ms.
stageHTMLElementthe image's parentThe box the canvas covers.
revealOnLoadbooleantrueReveal when the image finishes loading.

Returns

MemberDescription
reveal()Plays the reveal; resolves true when it finished.
destroy()Stops watching the image and cancels its animation.

CSS hooks

HookWhereMeaning
[data-fx-pending]stageThe image is loading and will be revealed: hide it with opacity only.
[data-animating], --pstageWhile the reveal runs.
--fx-coverstageCover color. Default: the stage background.

Sampling the image

Effects may declare uniform sampler2D uImage, vec2 uImageScale and float uHasImage. The image is uploaded once per source, flipped so its uv matches gl_FragCoord / uResolution; uImageScale accounts for object-fit: cover and contain (centered).

A cross-origin image without CORS cannot be read by WebGL. The library catches it and sets uHasImage to 0: the effect must then still work as a plain cover (the built-in one does). Add crossorigin="anonymous" on images served with CORS headers.

Behavior and accessibility