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.
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
| Option | Type | Default | Description |
|---|---|---|---|
renderer | Renderer | () => Renderer | required | The shared renderer. |
effect | EffectDef | inkImageEffect | An image effect (samples uImage) or any cover effect. |
duration | number | 1100 | Reveal duration in ms. |
stage | HTMLElement | the image's parent | The box the canvas covers. |
revealOnLoad | boolean | true | Reveal when the image finishes loading. |
Returns
| Member | Description |
|---|---|
reveal() | Plays the reveal; resolves true when it finished. |
destroy() | Stops watching the image and cancels its animation. |
CSS hooks
| Hook | Where | Meaning |
|---|---|---|
[data-fx-pending] | stage | The image is loading and will be revealed: hide it with opacity only. |
[data-animating], --p | stage | While the reveal runs. |
--fx-cover | stage | Cover 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
alt,srcset,sizes, lazy loading, decoding and layout are native and untouched.- The canvas is
aria-hiddenand exists only during the reveal; at the end it is transparent, so the real image takes over without a visible change. - Reduced motion, forced colors, more contrast, reduced transparency or no WebGL2: the image is never hidden, it loads natively.