Embla Carousel Autoplay with Progress Bar and Pause Control — Free Snippet

Embla Carousel Autoplay with Progress Bar and Pause Control · Carousels · Plain HTML, CSS & JS · Live preview

What's included

Features

Official Autoplay plugin
Configured with delay and stop behaviours.
Timer-synced progress bar
Driven by timerset and timeUntilNext.
Frozen on pause
timerstopped holds the bar in place.
Compositor-friendly animation
scaleX instead of width.
Pause/play button
With an updating aria-label.
Hover and focus pausing
Mouse and keyboard users treated alike.
Reduced-motion aware
Autoplay off by default for those who ask.
Slide counter
Current position out of total.

About this UI Snippet

Embla Autoplay Done Right — A Visible Timer and a Real Pause Button

Screenshot of the Embla Carousel Autoplay with Progress Bar and Pause Control snippet rendered live

Autoplaying carousels have a bad reputation, mostly earned by slides that change while you are reading and can't be stopped. The fix isn't to ban autoplay but to make it honest: show how long until the next slide, pause when someone is interacting, and give an explicit pause control. This snippet does all three with Embla's official Autoplay plugin.

Autoplay is a plugin instance

EmblaCarouselAutoplay({...}) creates the plugin, passed as the third argument to EmblaCarousel. stopOnInteraction: false makes a swipe restart the countdown instead of disabling autoplay permanently, and stopOnMouseEnter pauses while the pointer is over the slides.

The progress bar uses the plugin's clock

A common approach runs a separate setInterval for the bar, which drifts out of sync with the real slide change. Here the bar listens to the plugin's own events. autoplay:timerset fires whenever a countdown starts; the handler reads autoplay.timeUntilNext() and animates scaleX from the elapsed fraction to 1 over exactly the remaining time. autoplay:timerstopped freezes the bar in place, so a hover pause visibly holds the bar where it was.

The first event happens before you are listening

Plugins initialise inside the EmblaCarousel(...) call, so the very first autoplay:timerset fires before any embla.on(...) line runs. Without a manual startBar() after wiring the listeners, the bar would sit empty for the whole first slide. This applies to any plugin event you care about at startup.

Animating transform, not width

The fill animates transform: scaleX() with a left origin, which runs on the compositor and avoids layout work on every frame.

Pause for everyone

WCAG 2.2.2 requires moving content to be pausable. The play/pause button updates its label, and focus inside the carousel pauses it too, so keyboard users get the same break as mouse users. If the user prefers reduced motion, autoplay doesn't start at all (playOnInit: false) but remains available through the button.

Step by step

How to Use

  1. 1
    Load Embla and the pluginInclude embla-carousel and embla-carousel-autoplay UMD builds.
  2. 2
    Paste the snippetFour gradient announcement slides start cycling every four seconds.
  3. 3
    Hover or focusThe timer and progress bar pause; they resume when you leave.
  4. 4
    Press pauseAutoplay stops until you press play again.
  5. 5
    Change timingEdit DELAY; the bar follows automatically.

Real-world uses

Common Use Cases

Dashboard announcement banners
Show product news at the top of a dashboard, with a progress bar that reveals how long remains before the next slide changes.
Homepage promotions
Rotate offers with an honest timer, using the official Autoplay plugin's timerset and timeUntilNext values to drive the bar.
Kiosk and event screens
Run slides on an information display while still offering a pause and play button, with the bar frozen in place by timerstopped.
Rotating testimonials
Cycle quotes at a readable pace, pausing automatically on hover and keyboard focus so people can finish reading.
In-app onboarding tips
Rotate hints inside an application, respecting reduced-motion preferences and animating scaleX rather than width to stay compositor-friendly.
Related: Swiper Autoplay Progress Ring
Related: Embla Fade Hero
Crossfading slides with a plugin: Embla Carousel Fade Hero Slider.

Got questions?

Frequently Asked Questions

Load the embla-carousel-autoplay plugin, create it with EmblaCarouselAutoplay({ delay }) and pass it in the plugins array: EmblaCarousel(viewport, options, [autoplay]).

Listen to the autoplay:timerset event, read autoplay.timeUntilNext() and animate the bar over that many milliseconds. Listen to autoplay:timerstopped to freeze it. Using the plugin's own events keeps the bar exactly in sync.

When true (the default), autoplay stops permanently after the user drags. When false, dragging only restarts the countdown, and autoplay continues.

It can be if users can pause it. WCAG 2.2.2 requires a mechanism to pause moving content that lasts more than five seconds. Provide a pause button, pause on hover and focus, and respect prefers-reduced-motion.

Call autoplay.stop() and autoplay.play() on the plugin instance, or embla.plugins().autoplay.stop(). isPlaying() tells you the current state.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Share this snippet with an AI assistant like Claude and ask it to explain why the progress bar listens to timerset instead of running its own timer. Ask it to add per-slide durations (a longer delay for slides with more text), a circular timer instead of a bar, or pausing when the tab is hidden with the Page Visibility API. It can also check the pause behaviour against WCAG 2.2.2.

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 an autoplaying announcement carousel with Embla Carousel v8 and its Autoplay plugin (both loaded from a CDN as UMD scripts) in plain HTML, CSS and JavaScript.

Requirements:
- Four full-width gradient slides with a tag, heading and short text, looping.
- Autoplay every four seconds; dragging restarts the countdown rather than disabling autoplay; hovering pauses it.
- A progress bar under the carousel driven by the plugin's timer events: on timer start, animate from the elapsed fraction to full over the remaining time; on timer stop, freeze in place; reset on slide change. Animate transform scaleX, not width.
- A play/pause button with an updating aria-label, and pausing while keyboard focus is inside the carousel.
- Don't start autoplay when the user prefers reduced motion, but keep the play button working.
- Show a "2 / 4" slide counter.

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

<section class="eap" aria-roledescription="carousel" aria-label="Announcements">
  <div class="eap-viewport" id="eapViewport">
    <div class="eap-container">
      <article class="eap-slide" style="--c1:#4f46e5;--c2:#7c3aed"><span class="eap-tag">New</span><h3>Dark mode is here</h3><p>Every screen now follows your system theme, with a manual override in settings.</p></article>
      <article class="eap-slide" style="--c1:#0891b2;--c2:#0d9488"><span class="eap-tag">Webinar</span><h3>Scaling design systems</h3><p>Thursday 17:00 UTC — live Q&amp;A with the platform team.</p></article>
      <article class="eap-slide" style="--c1:#db2777;--c2:#e11d48"><span class="eap-tag">Offer</span><h3>30% off annual plans</h3><p>Upgrade before the end of the month to lock in the lower price.</p></article>
      <article class="eap-slide" style="--c1:#ea580c;--c2:#ca8a04"><span class="eap-tag">Guide</span><h3>Migrating to v5</h3><p>A step-by-step checklist with codemods for the breaking changes.</p></article>
    </div>
  </div>
  <div class="eap-bar">
    <button class="eap-play" id="eapPlay" type="button" aria-label="Pause autoplay">❚❚</button>
    <div class="eap-progress" aria-hidden="true"><div class="eap-fill" id="eapFill"></div></div>
    <div class="eap-num" id="eapNum"></div>
  </div>
</section>