# Layered Image Slider

> Multi-layer image transitions with custom easing — cinematic slides for hero sections and campaigns.

**Definition:** Layered Image Slider is a sliders UI animation from collections.html. It is built with HTML, CSS, JavaScript, and GSAP, ships with a live preview, and is free to copy into personal and commercial projects.

## Facts
- Category: Sliders
- Stack: HTML, CSS, JavaScript, and GSAP
- Added: 2026-08-01
- Canonical page: https://html.bosconvertpro.com/collection/layered-image-slider
- Full-page preview: https://html.bosconvertpro.com/collection/layered-image-slider/full
- Live demo: https://html.bosconvertpro.com/demos/layered-image-slider/index.html
- Markdown mirror: https://html.bosconvertpro.com/collection/layered-image-slider/index.md
- License: https://html.bosconvertpro.com/terms

## How to use
1. Open https://html.bosconvertpro.com/collection/layered-image-slider/full to preview the animation in full screen.
2. Copy index.html, styles.css, and script.js from the code tabs on https://html.bosconvertpro.com/collection/layered-image-slider.
3. Paste the files into your project and adjust timing, easing, and content as needed.

## FAQ
### What is Layered Image Slider?

Layered Image Slider is a sliders UI animation from collections.html. Multi-layer image transitions with custom easing — cinematic slides for hero sections and campaigns.

### What is Layered Image Slider built with?

Layered Image Slider is built with HTML, CSS, JavaScript, and GSAP. The source for every file is shown on the animation page and can be copied straight into your project.

### How do I use Layered Image Slider in my project?

Open the animation page (https://html.bosconvertpro.com/collection/layered-image-slider), preview it live, then copy index.html, styles.css, and script.js from the code tabs. Paste the files into your project and adjust timing, easing, and content as needed. A standalone demo runs at https://html.bosconvertpro.com/demos/layered-image-slider/index.html.

### Is Layered Image Slider free to use?

Yes. The Layered Image Slider demo code may be used in personal and commercial projects. You may not resell or redistribute the collection itself as a competing template pack. Full terms: https://html.bosconvertpro.com/terms.

## Source — index.html
````html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Layered Image Slider</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link rel="preconnect" href="https://wsrv.nl" />
    <link rel="preconnect" href="https://images.unsplash.com" />
    <link rel="preconnect" href="https://picsum.photos" />
    <link
      href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <section
      data-layered-slider-init
      data-layered-slider-autoplay="5"
      class="layered-slider"
    >
      <div class="layered-slider__container">
        <div class="layered-slider__bg-collection">
          <div class="layered-slider__bg-list">
            <div data-layered-slider-bg class="layered-slider__bg-item">
              <img alt="Dream Yachts" class="layered-slider__bg-img" data-slide-img="bg-0" />
            </div>
            <div data-layered-slider-bg class="layered-slider__bg-item">
              <img alt="Expedia" class="layered-slider__bg-img" data-slide-img="bg-1" />
            </div>
            <div data-layered-slider-bg class="layered-slider__bg-item">
              <img alt="Rimowa" class="layered-slider__bg-img" data-slide-img="bg-2" />
            </div>
            <div data-layered-slider-bg class="layered-slider__bg-item">
              <img alt="Nike F/W '25" class="layered-slider__bg-img" data-slide-img="bg-3" />
            </div>
            <div data-layered-slider-bg class="layered-slider__bg-item">
              <img alt="National Geographic" class="layered-slider__bg-img" data-slide-img="bg-4" />
            </div>
          </div>
        </div>

        <div class="layered-slider__bg-dark"></div>

        <div class="layered-slider__text-collection">
          <div class="layered-slider__text-list">
            <div data-layered-slider-title class="layered-slider__text-item">
              <a href="#" class="layered-slider__text-title">Dream Yachts</a>
            </div>
            <div data-layered-slider-title class="layered-slider__text-item">
              <a href="#" class="layered-slider__text-title">Expedia</a>
            </div>
            <div data-layered-slider-title class="layered-slider__text-item">
              <a href="#" class="layered-slider__text-title">Rimowa</a>
            </div>
            <div data-layered-slider-title class="layered-slider__text-item">
              <a href="#" class="layered-slider__text-title">Nike F/W '25</a>
            </div>
            <div data-layered-slider-title class="layered-slider__text-item">
              <a href="#" class="layered-slider__text-title">National Geographic</a>
            </div>
          </div>
        </div>

        <div data-layered-slider-mask class="layered-slider__mask-collection">
          <div class="layered-slider__mask-list">
            <div data-layered-slider-mask-item class="layered-slider__mask-item">
              <img alt="" draggable="false" class="layered-slider__mask-img" data-slide-img="mask-0" />
            </div>
            <div data-layered-slider-mask-item class="layered-slider__mask-item">
              <img alt="" draggable="false" class="layered-slider__mask-img" data-slide-img="mask-1" />
            </div>
            <div data-layered-slider-mask-item class="layered-slider__mask-item">
              <img alt="" draggable="false" class="layered-slider__mask-img" data-slide-img="mask-2" />
            </div>
            <div data-layered-slider-mask-item class="layered-slider__mask-item">
              <img alt="" draggable="false" class="layered-slider__mask-img" data-slide-img="mask-3" />
            </div>
            <div data-layered-slider-mask-item class="layered-slider__mask-item">
              <img alt="" draggable="false" class="layered-slider__mask-img" data-slide-img="mask-4" />
            </div>
          </div>
        </div>

        <div class="layered-slider__overlay">
          <div class="layered-slider__overlay-top">
            <div class="layered-slider__counter" aria-live="polite">
              <div class="layered-slider__counter-window">
                <div data-layered-slider-reel class="layered-slider__counter-reel"></div>
              </div>
            </div>
            <div class="layered-slider__progress">
              <div data-layered-slider-fill class="layered-slider__progress-inner"></div>
            </div>
            <span data-layered-slider-total class="layered-slider__span">05</span>
          </div>
          <div class="layered-slider__overlay-btm">
            <div class="layered-slider__nav">
              <button
                data-layered-slider-prev
                class="layered-slider__nav-button"
                type="button"
                aria-label="Previous"
              >
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  class="layered-slider__nav-icon"
                  aria-hidden="true"
                >
                  <path d="M15 6l-6 6 6 6"></path>
                </svg>
              </button>
              <button
                data-layered-slider-next
                class="layered-slider__nav-button"
                type="button"
                aria-label="Next"
              >
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  class="layered-slider__nav-icon"
                  aria-hidden="true"
                >
                  <path d="M9 6l6 6-6 6"></path>
                </svg>
              </button>
            </div>
          </div>
        </div>
      </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/Observer.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/CustomEase.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>
````

## Source — styles.css
````css
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  width: 100%;
  height: 100%;
}

body {
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  color: #fff;
  background: #0a0a0a;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

img {
  display: block;
  max-width: none;
}

.layered-slider {
  color: #fff;
  -webkit-user-select: none;
  user-select: none;
  position: relative;
  overflow: clip;
  width: 100%;
  height: 100%;
  height: 100dvh;
}

.layered-slider__container {
  width: 100%;
  height: 100%;
  position: relative;
}

.layered-slider__bg-collection {
  z-index: 0;
  position: absolute;
  inset: 0;
  overflow: clip;
}

.layered-slider__bg-list {
  width: 100%;
  height: 100%;
  position: relative;
}

.layered-slider__bg-item {
  width: 100%;
  height: 100%;
  position: absolute;
  inset: 0;
  opacity: 0;
  will-change: opacity, transform;
  background: #1a1a1a;
}

.layered-slider__bg-img {
  object-fit: cover;
  -webkit-user-select: none;
  user-select: none;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  position: relative;
}

.layered-slider__bg-dark {
  z-index: 2;
  background-color: rgba(0, 0, 0, 0.15);
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.layered-slider__text-collection {
  z-index: 3;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  display: flex;
  position: relative;
}

.layered-slider__text-list {
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  display: flex;
  position: relative;
}

.layered-slider__text-item {
  text-align: center;
  cursor: pointer;
  max-width: 80%;
  position: absolute;
  will-change: transform, opacity;
}

.layered-slider__text-title {
  letter-spacing: -0.05em;
  color: inherit;
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-size: 4em;
  font-weight: 500;
  line-height: 0.8;
  text-decoration: none;
  white-space: nowrap;
  display: block;
  position: relative;
}

.layered-slider__mask-collection {
  z-index: 3;
  aspect-ratio: 4 / 3;
  border-radius: 0.85em;
  width: min(18em, 42vw);
  position: absolute;
  bottom: clamp(5.5rem, 10vh, 7.5rem);
  left: 50%;
  overflow: hidden;
  transform: translateX(-50%);
  box-shadow:
    0 20px 48px rgba(0, 0, 0, 0.35),
    0 0 0 1px rgba(255, 255, 255, 0.08);
}

.layered-slider__mask-list {
  position: relative;
  width: 100%;
  height: 100%;
}

.layered-slider__mask-item {
  flex: none;
  width: 100%;
  height: 100%;
  position: absolute;
  inset: 0;
  overflow: hidden;
  will-change: transform;
  background: #222;
}

.layered-slider__mask-img {
  pointer-events: none;
  object-fit: cover;
  -webkit-user-select: none;
  user-select: none;
  width: 100%;
  height: 100%;
  display: block;
  position: relative;
  background: #222;
}

.layered-slider__overlay {
  z-index: 4;
  pointer-events: none;
  flex-flow: column;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  max-width: 120em;
  margin-left: auto;
  margin-right: auto;
  padding: 2em;
  display: flex;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.layered-slider__overlay-top {
  gap: 0.5em;
  pointer-events: auto;
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.layered-slider__counter {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 1.75em;
}

.layered-slider__counter-window {
  --counter-line: 1.1em;
  position: relative;
  overflow: hidden;
  height: var(--counter-line);
  width: 1.75em;
  mask-image: linear-gradient(
    to bottom,
    transparent 0%,
    #000 22%,
    #000 78%,
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0%,
    #000 22%,
    #000 78%,
    transparent 100%
  );
}

.layered-slider__counter-reel {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  will-change: transform;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
}

.layered-slider__counter-item {
  flex: 0 0 var(--counter-line);
  height: var(--counter-line);
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: rgba(255, 255, 255, 0.9);
  line-height: 1;
  user-select: none;
  white-space: nowrap;
}

.layered-slider__span {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.85);
  min-width: 1.5em;
  text-align: center;
  display: inline-block;
  line-height: 1.15;
  position: relative;
}

.layered-slider__progress {
  background-color: rgba(255, 255, 255, 0.25);
  border-radius: 10em;
  width: 6em;
  height: 2px;
  overflow: hidden;
  position: relative;
}

.layered-slider__progress-inner {
  transform-origin: 0% 50%;
  width: 100%;
  height: 100%;
  background-color: #fff;
  transform: scale3d(0, 1, 1);
  position: relative;
}

.layered-slider__overlay-btm {
  justify-content: flex-end;
  align-items: center;
  width: 100%;
  display: flex;
  position: relative;
}

.layered-slider__nav {
  gap: 0.5em;
  pointer-events: auto;
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.layered-slider__nav-button {
  background-color: transparent;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 100em;
  justify-content: center;
  align-items: center;
  width: 3em;
  height: 3em;
  padding: 0.75em;
  display: flex;
  position: relative;
  transition:
    background-color 0.25s ease,
    border-color 0.25s ease;
}

.layered-slider__nav-button:hover {
  background-color: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.5);
}

.layered-slider__nav-button:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.7);
  outline-offset: 3px;
}

.layered-slider__nav-icon {
  width: 100%;
  height: 100%;
  display: block;
  position: relative;
}

@media screen and (max-width: 767px) {
  .layered-slider__text-title {
    font-size: 2.5em;
  }

  .layered-slider__overlay {
    padding: 1em;
  }

  .layered-slider__overlay-btm {
    justify-content: center;
    align-items: center;
  }

  .layered-slider__nav {
    justify-content: space-between;
    align-items: center;
    width: 100%;
  }

  .layered-slider__mask-collection {
    bottom: 5.5rem;
    width: min(14em, 58vw);
  }
}

@media screen and (max-width: 479px) {
  .layered-slider__mask-collection {
    width: min(12.5em, 58vw);
  }
}
````

## Source — script.js
````javascript
gsap.registerPlugin(Observer, CustomEase);

CustomEase.create("smooth", "M0,0 C0.625,0.05 0,1 1,1");

function proxied(photoPath, w, h) {
  const base = photoPath.replace(/^https?:\/\//, "");
  return `https://wsrv.nl/?url=${encodeURIComponent(base)}&w=${w}&h=${h}&fit=cover&q=80`;
}

const IMAGE_SOURCES = {
  "bg-0": [
    proxied("images.unsplash.com/photo-1567899378494-47b22a2ae96a", 2000, 1333),
    proxied("images.unsplash.com/photo-1569263979104-865ab7cd8d13", 2000, 1333),
    "https://picsum.photos/id/1016/2000/1333.jpg",
  ],
  "mask-0": [
    proxied("images.unsplash.com/photo-1544551763-46a013bb70d5", 1200, 900),
    proxied("images.unsplash.com/photo-1559827260-dc66d52bef19", 1200, 900),
    "https://picsum.photos/id/1015/1200/900.jpg",
  ],
  "bg-1": [
    proxied("images.unsplash.com/photo-1436491865332-7a61a109cc05", 2000, 1333),
    proxied("images.unsplash.com/photo-1474302770737-173a53727be0", 2000, 1333),
    "https://picsum.photos/id/1036/2000/1333.jpg",
  ],
  "mask-1": [
    proxied("images.unsplash.com/photo-1488646953014-85cb44e25828", 1200, 900),
    proxied("images.unsplash.com/photo-1469854523086-cc02fe5d8800", 1200, 900),
    "https://picsum.photos/id/1080/1200/900.jpg",
  ],
  "bg-2": [
    proxied("images.unsplash.com/photo-1540962351504-03099f09e6dc", 2000, 1333),
    proxied("images.unsplash.com/photo-1556388158-158ea5ccacbd", 2000, 1333),
    "https://picsum.photos/id/164/2000/1333.jpg",
  ],
  "mask-2": [
    proxied("images.unsplash.com/photo-1565026057447-bc90a3dceb87", 1200, 900),
    proxied("images.unsplash.com/photo-1553062407-98eeb64c6a62", 1200, 900),
    "https://picsum.photos/id/201/1200/900.jpg",
  ],
  "bg-3": [
    proxied("images.unsplash.com/photo-1552674605-db6ffd4facb5", 2000, 1333),
    proxied("images.unsplash.com/photo-1461896836934-ffe607ba6851", 2000, 1333),
    "https://picsum.photos/id/1011/2000/1333.jpg",
  ],
  "mask-3": [
    proxied("images.unsplash.com/photo-1542291026-7eec264c27ff", 1200, 900),
    proxied("images.unsplash.com/photo-1606107557195-0e29a4b5b4aa", 1200, 900),
    "https://picsum.photos/id/433/1200/900.jpg",
  ],
  "bg-4": [
    proxied("images.unsplash.com/photo-1506905925346-21bda4d32df4", 2000, 1333),
    proxied("images.unsplash.com/photo-1464822759023-fed622ff2c3b", 2000, 1333),
    "https://picsum.photos/id/1018/2000/1333.jpg",
  ],
  "mask-4": [
    proxied("images.unsplash.com/photo-1469474968028-56623f02e42e", 1200, 900),
    proxied("images.unsplash.com/photo-1441974231531-c6227db76b6e", 1200, 900),
    "https://picsum.photos/id/1039/1200/900.jpg",
  ],
};

function loadImageWithFallback(img, sources) {
  return new Promise((resolve) => {
    if (!img || !sources || !sources.length) {
      resolve();
      return;
    }

    let index = 0;
    let timer = null;

    const cleanup = (probe) => {
      if (timer) clearTimeout(timer);
      probe.onload = null;
      probe.onerror = null;
    };

    const tryNext = () => {
      if (index >= sources.length) {
        resolve();
        return;
      }

      const url = sources[index++];
      const probe = new Image();

      probe.onload = () => {
        cleanup(probe);
        img.src = url;
        resolve();
      };

      probe.onerror = () => {
        cleanup(probe);
        tryNext();
      };

      timer = setTimeout(() => {
        cleanup(probe);
        tryNext();
      }, 5000);

      probe.src = url;
    };

    tryNext();
  });
}

function loadAllSlideImages(root) {
  const nodes = [...root.querySelectorAll("[data-slide-img]")];
  return Promise.all(
    nodes.map((img) => {
      const key = img.getAttribute("data-slide-img");
      return loadImageWithFallback(img, IMAGE_SOURCES[key] || []);
    })
  );
}

function pad(n) {
  return String(n).padStart(2, "0");
}

function initLayeredImageSlider() {
  document.querySelectorAll("[data-layered-slider-init]").forEach((root) => {
    if (root._layeredSlider) root._layeredSlider.destroy();

    loadAllSlideImages(root);

    const titles = [...root.querySelectorAll("[data-layered-slider-title]")];
    if (!titles.length) return;

    const count = titles.length;
    const backgrounds = [...root.querySelectorAll("[data-layered-slider-bg]")];
    const maskItems = [...root.querySelectorAll("[data-layered-slider-mask-item]")];
    const maskFrame = root.querySelector("[data-layered-slider-mask]");
    const fill = root.querySelector("[data-layered-slider-fill]");
    const totalEl = root.querySelector("[data-layered-slider-total]");
    const prevBtn = root.querySelector("[data-layered-slider-prev]");
    const nextBtn = root.querySelector("[data-layered-slider-next]");
    const counterReel = root.querySelector("[data-layered-slider-reel]");
    const counterRoot = root.querySelector(".layered-slider__counter");
    const controls = [...new Set([...titles, ...root.querySelectorAll("a, button")])];

    const autoplayAttr = root.getAttribute("data-layered-slider-autoplay");
    const autoplay = autoplayAttr !== null ? parseFloat(autoplayAttr) : 5;
    const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
    const clamp = gsap.utils.clamp;
    const wrap = (distance) => distance - count * Math.round(distance / count);

    const transitionDuration = 1;
    const backgroundZoom = 0;
    const titleGap = 0.5;
    const titleSpacing = 40;

    if (totalEl) totalEl.textContent = pad(count);

    if (counterReel) {
      counterReel.innerHTML = "";
      const lead = document.createElement("div");
      lead.className = "layered-slider__counter-item";
      lead.textContent = pad(count);
      lead.setAttribute("aria-hidden", "true");
      counterReel.appendChild(lead);

      for (let i = 1; i <= count; i++) {
        const item = document.createElement("div");
        item.className = "layered-slider__counter-item";
        item.textContent = pad(i);
        counterReel.appendChild(item);
      }

      const trail = document.createElement("div");
      trail.className = "layered-slider__counter-item";
      trail.textContent = pad(1);
      trail.setAttribute("aria-hidden", "true");
      counterReel.appendChild(trail);
    }

    let titleStep = 0;
    let maskStep = 0;
    let itemHeight = 0;

    const measure = () => {
      const widestTitle = Math.max(...titles.map((title) => title.offsetWidth));
      titleStep = Math.max(root.clientWidth * titleGap, widestTitle + titleSpacing);
      maskStep = maskFrame ? maskFrame.clientWidth : root.clientWidth;
      if (counterReel && counterReel.children[0]) {
        itemHeight = counterReel.children[0].getBoundingClientRect().height;
      }
    };

    measure();

    const state = { progress: 0 };
    let activeIndex = -1;
    let current = 0;

    const setActive = (previousIndex, index) => {
      [backgrounds, titles, maskItems].forEach((list) => {
        if (previousIndex >= 0 && list[previousIndex]) {
          list[previousIndex].removeAttribute("data-active");
        }
        if (list[index]) list[index].setAttribute("data-active", "");
      });
    };

    const setCounterY = (progress) => {
      if (!counterReel || !itemHeight) return;
      gsap.set(counterReel, { y: -(progress + 1) * itemHeight });
    };

    const normalizePosition = () => {
      if (current >= count || current < 0) {
        current = ((current % count) + count) % count;
        state.progress = current;
        setCounterY(current);
      }
    };

    const render = (progress) => {
      const centeredIndex = ((Math.round(progress) % count) + count) % count;

      for (let i = 0; i < count; i++) {
        const offset = wrap(i - progress);
        const distance = Math.abs(offset);
        const background = backgrounds[i];

        if (background) {
          const backgroundOpacity = clamp(0, 1, 1 - distance);
          gsap.set(background, {
            opacity: backgroundOpacity,
            scale: 1 + backgroundZoom - backgroundZoom * backgroundOpacity,
            zIndex: Math.round(backgroundOpacity * 100),
          });
        }

        gsap.set(titles[i], {
          x: offset * titleStep,
          opacity: i === centeredIndex ? 1 : 0.4,
          pointerEvents: "auto",
        });

        const maskItem = maskItems[i];
        if (maskItem) {
          gsap.set(maskItem, { x: offset * maskStep });
        }
      }

      setCounterY(progress);

      if (centeredIndex !== activeIndex) {
        const previousIndex = activeIndex;
        activeIndex = centeredIndex;
        setActive(previousIndex, centeredIndex);
        if (counterRoot) {
          counterRoot.setAttribute(
            "aria-label",
            `Slide ${pad(centeredIndex + 1)} of ${pad(count)}`
          );
        }
      }
    };

    let hovering = 0;
    let autoTween = null;

    const startAutoplay = () => {
      if (!autoTween) return;
      autoTween.restart();
      if (hovering > 0) autoTween.pause();
    };

    let slideTween = null;

    function goTo(delta) {
      current += delta;
      if (slideTween) slideTween.kill();
      slideTween = gsap.to(state, {
        progress: current,
        duration: reduced ? 0 : transitionDuration,
        ease: "smooth",
        onUpdate: () => render(state.progress),
        onComplete: () => {
          normalizePosition();
          render(state.progress);
        },
      });
      startAutoplay();
    }

    function goToIndex(i) {
      const delta = wrap(i - current);
      if (delta !== 0) goTo(delta);
    }

    if (autoplay > 0 && !reduced && fill) {
      gsap.set(fill, { scaleX: 0, transformOrigin: "left center" });
      autoTween = gsap.to(fill, {
        scaleX: 1,
        duration: autoplay,
        ease: "none",
        paused: true,
        onComplete: () => goTo(1),
      });
    }

    let gestureUsed = false;
    const observer = Observer.create({
      target: root,
      type: "touch,pointer",
      dragMinimum: 10,
      tolerance: 25,
      lockAxis: true,
      onDragStart() {
        gestureUsed = false;
      },
      onLeft() {
        if (!gestureUsed) {
          gestureUsed = true;
          goTo(1);
        }
      },
      onRight() {
        if (!gestureUsed) {
          gestureUsed = true;
          goTo(-1);
        }
      },
    });

    const onPrev = () => goTo(-1);
    const onNext = () => goTo(1);

    if (prevBtn) prevBtn.addEventListener("click", onPrev);
    if (nextBtn) nextBtn.addEventListener("click", onNext);

    const onTitleClick = (e) => {
      const i = titles.indexOf(e.currentTarget);
      if (i === activeIndex) return;
      e.preventDefault();
      goToIndex(i);
    };

    titles.forEach((title) => title.addEventListener("click", onTitleClick));

    const onEnter = () => {
      hovering++;
      if (autoTween) autoTween.pause();
    };

    const onLeave = () => {
      hovering = Math.max(0, hovering - 1);
      if (autoTween && hovering === 0) autoTween.resume();
    };

    controls.forEach((el) => {
      el.addEventListener("pointerenter", onEnter);
      el.addEventListener("pointerleave", onLeave);
    });

    const onResize = () => {
      measure();
      setCounterY(state.progress);
      render(state.progress);
    };

    window.addEventListener("resize", onResize);
    if (document.fonts) document.fonts.ready.then(onResize);

    root.querySelectorAll("img").forEach((img) => {
      if (img.complete) return;
      img.addEventListener("load", () => {}, { once: true });
    });

    render(0);
    startAutoplay();

    root._layeredSlider = {
      goTo,
      destroy() {
        observer.kill();
        if (slideTween) slideTween.kill();
        if (autoTween) autoTween.kill();
        window.removeEventListener("resize", onResize);
        if (prevBtn) prevBtn.removeEventListener("click", onPrev);
        if (nextBtn) nextBtn.removeEventListener("click", onNext);
        titles.forEach((title) => title.removeEventListener("click", onTitleClick));
        controls.forEach((el) => {
          el.removeEventListener("pointerenter", onEnter);
          el.removeEventListener("pointerleave", onLeave);
        });
        root._layeredSlider = null;
      },
    };
  });
}

document.addEventListener("DOMContentLoaded", () => {
  initLayeredImageSlider();
});
````
