Embla Carousel Parallax Slides — Free Scroll-Linked Parallax Snippet

Embla Carousel Parallax Slides · Carousels · Plain HTML, CSS & JS · Live preview

What's included

Features

Scroll-linked parallax
Driven by scrollProgress and snap positions.
Loop-safe
loopPoints correction prevents jumps at the wrap.
Multi-slide snaps
slideRegistry maps snaps to their slides.
In-view optimisation
Only visible slides update while scrolling.
Consistent strength
Tween factor scales with snap count.
Edge-proof layers
A 30% bleed hides the image edges.
Reduced-motion aware
The effect is skipped when requested.
Keyboard friendly
Arrow buttons and slideFocus handling.

About this UI Snippet

Embla Carousel Parallax — Images That Lag Behind Their Frames

Screenshot of the Embla Carousel Parallax Slides snippet rendered live

Parallax in a carousel means the photo inside each card moves a little slower than the card itself while you drag, so the image seems to sit behind a window. It's subtle, and that subtlety is why it looks expensive. The technique is worth learning because the same maths drives scale, opacity and rotation effects.

Where each slide is relative to the centre

Embla exposes scrollProgress(), a number from 0 to 1 for the whole track, and scrollSnapList(), the progress value at which each snap is centred. Subtracting gives diff for each snap: 0 when centred, positive to the right, negative to the left. Translating the inner image by diff * -tweenFactor * 100% moves it against the scroll direction.

Why the image layer is wider than the frame

The frame clips with overflow: hidden, and the image layer is extended 30% beyond each side (inset: 0 -30%). Without that bleed, the moving image would reveal an empty edge.

slideRegistry maps snaps to slides

A snap can contain more than one slide. engine.slideRegistry[snapIndex] lists the slide indexes for each snap, so every slide in a snap gets the same offset.

The loop correction

With loop: true, Embla repositions slides at the ends to create the seamless loop. A repositioned slide would compute its diff from the far side of the track and jump. The slideLooper.loopPoints check detects slides currently moved by the looper and recalculates diff across the wrap.

Only work on visible slides

During scroll events the loop skips slides not in slidesInView(), keeping the per-frame work small. The tween also runs on reInit and slideFocus so tabbing through slides stays correct.

Motion preferences

If the user prefers reduced motion, the tween does nothing and the carousel behaves normally.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet into an AI assistant like Claude and ask it to walk through how diff is calculated and why the loop correction is needed. Ask it to turn the parallax into a vertical carousel, add a subtle zoom to the centred slide, or apply the tween to caption text so it moves in the opposite direction. It can also explain the risks of relying on internalEngine() across Embla upgrades.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a looping parallax photo carousel with Embla Carousel v8 (loaded from a CDN as UMD) in plain HTML, CSS and JavaScript on a dark background.

Requirements:
- Six photo slides at 72% width with rounded frames that clip their content, and captions.
- Inside each frame, an image layer that extends 30% beyond each side.
- On scroll, compute each snap's distance from the current scroll progress and translate that slide's image layer by distance × −factor × 100%, with the factor scaled by the number of snaps.
- Use the engine's slide registry to map snaps to slides, update only slides in view during scroll, and correct distances for slides that the loop has moved to the other end.
- Re-run the calculation on reInit and slideFocus.
- Add previous and next buttons, and skip the effect entirely when the user prefers reduced motion.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="epx">
  <div class="epx-viewport" id="epxViewport">
    <div class="epx-container" id="epxContainer"></div>
  </div>
  <div class="epx-foot">
    <span>Drag, swipe or use the arrows — images move slower than their frames</span>
    <div>
      <button type="button" id="epxPrev" aria-label="Previous">←</button>
      <button type="button" id="epxNext" aria-label="Next">→</button>
    </div>
  </div>
</div>

Step by step

How to Use

  1. 1
    Load EmblaInclude embla-carousel.umd.js from the CDN.
  2. 2
    Paste the snippetSix looping photo cards render.
  3. 3
    Drag slowlyWatch each image shift inside its frame as it moves.
  4. 4
    Tune the strengthRaise or lower TWEEN_FACTOR_BASE; widen the layer bleed if edges appear.
  5. 5
    Reuse the mathsSwap translateX for scale or opacity to build other effects.

Real-world uses

Common Use Cases

Travel and hospitality
Destination cards with depth.
Portfolios
Project covers with a premium feel.
Product launches
Hero-style feature slides.
Editorial sites
Story cards that invite swiping.
Learning Embla
The core tween pattern behind many effects.
Related: Embla Scale-on-Focus
The same maths applied to scale: Embla Carousel Scale-on-Focus.
Related: Swiper Parallax Hero Slides
Swiper's built-in parallax module: Swiper Parallax Hero Slides.

Got questions?

Frequently Asked Questions

On every scroll event, compute each snap's distance from the current position with scrollSnapList()[i] - scrollProgress(), then translate an inner image layer by that distance times a negative factor. The frame clips the image, which moves more slowly than the slide.

With loop enabled, Embla moves slides to the other end of the track. Their distance must be measured across the wrap. Check engine.slideLooper.loopPoints for the slide and adjust the distance using 1 + progress or 1 - progress depending on the direction.

internalEngine() exposes Embla's internal parts, such as slideRegistry and slideLooper. The official parallax and scale examples use it, but it isn't covered by semantic versioning guarantees, so re-test after upgrading Embla.

The image moves sideways inside its frame. Extending it beyond the frame on both sides gives it room to move without revealing a gap at the edge.

It only updates transforms of slides in view, and transforms are handled by the compositor. For heavy pages you can also skip the effect on low-power devices or when the user prefers reduced motion.