Smooth Ken Burns
Your photo slowly zooms and drifts inside its frame on a 15-second loop; hovering or the pause button stops it, and it stays still for anyone who reduces motion.
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



How it works
The CSS animation kbu-drift runs on the <img class="kbu-image">: over --kbu-duration (15 s), the image goes through four framings (scaled from 1 to 1.25, shifted by a few per cent) and back to the start, with an ease-in-out that avoids jolts. The frame clips the overflow. No script moves the image.
A small round button at the bottom right (.kbu-toggle) pauses and resumes the animation; that is the JavaScript's only job, wiring each root separately. Hovering the image also pauses it, as in the original version, and the “Auto-animating” badge flags the motion.
The frame adapts to the available width up to --kbu-width and keeps --kbu-ratio. For a full-screen background, set the root to position: absolute; inset: 0 and use --kbu-width: 100%.
Accessibility
- Pause: the button is a real
<button>named “Pause the animation”;aria-pressed="true"says the image is stopped. Motion lasting more than five seconds must be stoppable (WCAG 2.2.2): that is what it is for. - Reduced motion: under
prefers-reduced-motion: reduce, the animation is removed, the image stays at its starting framing, and the badge and button disappear since there is nothing left to stop. - Screen readers: the image keeps its alt text; the caption is hidden. The badge stays readable: remove it if it adds nothing to your page.
- Hover: the mouse stops the image while it is over it; the button stops it for good, even when the mouse leaves.
Browser compatibility
CSS animation, aspect-ratio and under 1 KB of JavaScript for the button, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26.5: two copies with only the 2nd paused, script pasted twice without error, button on the keyboard, reduced motion (no animation), no overflow at 390 px.
Without JavaScript, the animation runs and stops on hover, but the button does nothing: hide it in that case. Without aspect-ratio, the frame takes the image's height.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0186-root">
<div class="kbu-frame">
<img class="kbu-image" src="your-photo.jpg" alt="Photo description">
<span class="kbu-label" aria-hidden="true">Ken Burns</span>
<span class="kbu-badge">Auto-animating</span>
<button type="button" class="kbu-toggle" aria-pressed="false" aria-label="Pause the animation"></button>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Image (HTML — src of .kbu-image) |
violet grid pattern (inline SVG) | Your photo and its alt. The image grows up to 125%: a file 1.3 times wider than the frame stays sharp. |
--kbu-duration |
15s | Length of a full cycle; 8s for visible motion, 25s for an almost still background. |
--kbu-width · --kbu-ratio · --kbu-radius |
280px · 7 / 5 · 0.75rem | Maximum width, proportions and rounding of the frame. |
Framings (@keyframes kbu-drift) |
1 → 1.2 → 1.15 → 1.25 | Edit the scale() and translate() of the 25%, 50% and 75% steps to aim at another part of the photo. |
.kbu-badge badge · .kbu-label caption |
“Auto-animating” · “Ken Burns” | Optional elements: delete the <span> tags on a real photo. |
| Pause on hover | on | For a background, add pointer-events: none to the frame and pointer-events: auto to the button: only the button pause remains. |