# Scroll Wheel Animation

> Scroll-scrubbed brand wheel rotating client names around an arc, highlighting each with logo crossfades and descriptions.

**Definition:** Scroll Wheel Animation is a scroll 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: Scroll
- Stack: HTML, CSS, JavaScript, and GSAP
- Added: 2026-09-06
- Canonical page: https://html.bosconvertpro.com/collection/scroll-wheel-animation
- Full-page preview: https://html.bosconvertpro.com/collection/scroll-wheel-animation/full
- Live demo: https://html.bosconvertpro.com/demos/scroll-wheel-animation/index.html
- Markdown mirror: https://html.bosconvertpro.com/collection/scroll-wheel-animation/index.md
- License: https://html.bosconvertpro.com/terms

## How to use
1. Open https://html.bosconvertpro.com/collection/scroll-wheel-animation/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/scroll-wheel-animation.
3. Paste the files into your project and adjust timing, easing, and content as needed.

## FAQ
### What is Scroll Wheel Animation?

Scroll Wheel Animation is a scroll UI animation from collections.html. Scroll-scrubbed brand wheel rotating client names around an arc, highlighting each with logo crossfades and descriptions.

### What is Scroll Wheel Animation built with?

Scroll Wheel Animation 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 Scroll Wheel Animation in my project?

Open the animation page (https://html.bosconvertpro.com/collection/scroll-wheel-animation), 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/scroll-wheel-animation/index.html.

### Is Scroll Wheel Animation free to use?

Yes. The Scroll Wheel Animation 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>They Trust Us — Scroll-Driven Brand Wheel Animation (GSAP ScrollTrigger)</title>
  <meta
    name="description"
    content="They Trust Us section animation: 11 client brand names (Louis Vuitton, Chanel, Dior and more) rotate around a scroll-scrubbed arc wheel inside a pinned 300vh section. Scrolling highlights each brand in cream, crossfades its logo with a blur transition, and reveals the description — built with HTML, CSS, JavaScript, GSAP ScrollTrigger and Lenis smooth scroll, recreated from fromanother.love."
  />
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link
    href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Instrument+Serif:ital@0;1&display=swap"
    rel="stylesheet"
  />
  <link rel="stylesheet" href="styles.css" />
</head>
<body>

  <section id="collaborations">
    <div class="collaborations">
      <div class="collaborations_content">

        <div class="collaborations_content_index">
          <div>
            <div class="sectionIndex">
              <span data-number="true">5</span>
              <span data-separator="true"></span>
              <span data-title="true">They trust us</span>
            </div>
          </div>
        </div>

        <div class="collaborations_content_description">
          <div>
            <div class="description_inner">
              <p>From multinational companies to independent brands, we have been fortunate to work in a diversity of projects and clients.</p>
            </div>
          </div>
        </div>

        <div class="collaborations_content_grid">
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/lv.png" alt="Louis Vuitton" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/oneplus.png" alt="OnePlus" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/chanel.png" alt="Chanel" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/glossier.png" alt="Glossier" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/lamer.png" alt="La Mer" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/loreal.png" alt="L'Oreal" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/dior.png" alt="Dior" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/oppo.png" alt="Oppo" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/thenorthface.png" alt="The North Face" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/88rising.png" alt="88rising" /></div>
          </div>
          <div class="collaborations_content_grid_item">
            <div class="collaborations_content_grid_item_logo"><img src="assets/logos/oribe.png" alt="Oribe" /></div>
          </div>
        </div>

        <div class="collaborations_content_arc" style="--total-items:11">
          <div class="collaborations_content_arc_item active" style="--index:0">Louis Vuitton</div>
          <div class="collaborations_content_arc_item" style="--index:1">OnePlus</div>
          <div class="collaborations_content_arc_item" style="--index:2">Chanel</div>
          <div class="collaborations_content_arc_item" style="--index:3">Glossier</div>
          <div class="collaborations_content_arc_item" style="--index:4">La Mer</div>
          <div class="collaborations_content_arc_item" style="--index:5">L'Oreal</div>
          <div class="collaborations_content_arc_item" style="--index:6">Dior</div>
          <div class="collaborations_content_arc_item" style="--index:7">Oppo</div>
          <div class="collaborations_content_arc_item" style="--index:8">The North Face</div>
          <div class="collaborations_content_arc_item" style="--index:9">88rising</div>
          <div class="collaborations_content_arc_item" style="--index:10">Oribe</div>
        </div>

        <div class="collaborations_content_logos">
          <div class="collaborations_content_logos_item"><img src="assets/logos/lv.png" alt="Louis Vuitton" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/oneplus.png" alt="OnePlus" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/chanel.png" alt="Chanel" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/glossier.png" alt="Glossier" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/lamer.png" alt="La Mer" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/loreal.png" alt="L'Oreal" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/dior.png" alt="Dior" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/oppo.png" alt="Oppo" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/thenorthface.png" alt="The North Face" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/88rising.png" alt="88rising" /></div>
          <div class="collaborations_content_logos_item"><img src="assets/logos/oribe.png" alt="Oribe" /></div>
        </div>

      </div>
    </div>
  </section>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/ScrollTrigger.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/lenis@1.1.18/dist/lenis.min.js"></script>
  <script src="script.js"></script>
</body>
</html>
````

## Source — styles.css
````css
/* ============================================================
   "They trust us" — collaborations section
   Faithful port of fromanother.love (CSS modules + rem scaling)
   ============================================================ */

:root {
  --easeOutQuart: cubic-bezier(0.165, 0.84, 0.44, 1);
  --brand-yellow: #fffde2;
  --brand-dark-blue: #051236;
  --brand-blue: #1740a9;
  --text-secondary: rgba(255, 253, 226, 0.6);
  --text-disabled: rgba(255, 253, 226, 0.3);
  --font-twk: "Inter", "Helvetica Neue", Arial, sans-serif;
  --font-herbik: "Instrument Serif", Georgia, serif;
}

/* rem scaling (identical to the original site) */
body,
html {
  font-size: max(2.6666666667vw, 1px);
}
@media (min-width: 768px) {
  body,
  html {
    font-size: max(1.3020833333vw, 1px);
  }
}
@media (min-width: 1200px) {
  body,
  html {
    font-size: max(0.5208333333vw, 1px);
  }
}

html {
  font-smoothing: antialiased;
  -webkit-font-smoothing: antialiased;
  scroll-behavior: unset !important;
  overscroll-behavior: none !important;
}
html.lenis,
html.lenis body {
  height: auto;
}
.lenis.lenis-smooth {
  scroll-behavior: auto !important;
}

body {
  font-family: var(--font-twk), sans-serif;
  color: var(--brand-yellow);
  background-color: #060606;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  --gap-x: 1.6rem;
  --padding-x: 1.6rem;
  --columns: 6;
  margin: 0;
  line-height: 1.5;
}
@media (min-width: 768px) {
  body {
    --gap-x: 3.2rem;
    --padding-x: 3.2rem;
    --columns: 8;
  }
}
@media (min-width: 1200px) {
  body {
    --gap-x: 2.4rem;
    --padding-x: 4.8rem;
    --columns: 14;
  }
}

body ::-webkit-scrollbar,
html ::-webkit-scrollbar {
  display: none;
}

/* ============================================================
   Collaborations section
   ============================================================ */
.collaborations {
  position: relative;
}
@media (min-width: 1200px) {
  .collaborations {
    padding-top: 0;
    height: 300svh;
  }
}

.collaborations_content {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding-top: 18rem;
  padding-bottom: 14.5rem;
}
@media (min-width: 768px) {
  .collaborations_content {
    padding-top: 31.4rem;
    padding-bottom: 14.8rem;
  }
}
@media (min-width: 1200px) {
  .collaborations_content {
    padding: 0;
    position: sticky;
    top: 0;
    left: 0;
    height: 100svh;
  }
}

.collaborations_content {
  background-color: #060606;
}

/* premium black gradient: layered neutral glows + vignette */
.collaborations_content::before {
  content: "";
  position: absolute;
  inset: -6%;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.65), transparent 20%, transparent 80%, rgba(0, 0, 0, 0.7)),
    radial-gradient(48rem 48rem at 30% 44%, rgba(255, 255, 255, 0.07), transparent 70%),
    radial-gradient(62rem 62rem at 62% 50%, rgba(255, 255, 255, 0.1), transparent 70%),
    radial-gradient(90rem 60rem at 86% 52%, rgba(255, 253, 226, 0.06), transparent 70%),
    radial-gradient(130rem 90rem at 50% 112%, rgba(0, 0, 0, 0.75), transparent 62%);
  pointer-events: none;
  z-index: 0;
  animation: bgDrift 18s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes bgDrift {
  from {
    transform: translate3d(-1.2%, -0.8%, 0) scale(1);
  }
  to {
    transform: translate3d(1.2%, 0.8%, 0) scale(1.035);
  }
}

/* film grain */
.collaborations_content::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='280' height='280'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  background-size: 28rem 28rem;
  opacity: 0.05;
  pointer-events: none;
  z-index: 4;
}

/* ------- index (mobile / tablet only) ------- */
.collaborations_content_index {
  width: max-content;
  margin: 0 auto 4rem;
  position: relative;
  z-index: 3;
}
@media (min-width: 1200px) {
  .collaborations_content_index {
    display: none;
  }
}
.sectionIndex {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  font-family: var(--font-herbik);
  font-size: 1.2rem;
  font-weight: 500;
  line-height: normal;
  font-style: italic;
  letter-spacing: normal;
  color: var(--brand-yellow);
}
.sectionIndex [data-separator] {
  display: inline-block;
  height: 0.1rem;
  width: 1.6rem;
  background-color: var(--brand-yellow);
}

/* ------- description ------- */
.collaborations_content_description {
  width: 31.4rem;
  margin: 0 auto;
  color: var(--text-secondary);
  font: 400 1.4rem/1.4em var(--font-twk);
  letter-spacing: -0.028rem;
  text-align: center;
  position: relative;
  z-index: 3;
}
@media (min-width: 768px) {
  .collaborations_content_description {
    width: 52rem;
    font: 400 1.6rem/1.4em var(--font-twk);
    letter-spacing: -0.032rem;
    margin-bottom: 3.2rem;
  }
}
@media (min-width: 1200px) {
  .collaborations_content_description {
    text-align: left;
    font: 400 1.8rem/1.4em var(--font-twk);
    letter-spacing: -0.02rem;
    letter-spacing: -0.036rem;
    width: 37.2rem;
    padding: 0;
    position: absolute;
    top: 50%;
    left: 150rem;
    transform: translateY(-1.8rem);
    margin: 0;
  }
}
.collaborations_content_description p {
  margin: 0;
}

/* ------- logo grid (mobile / tablet only) ------- */
.collaborations_content_grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.6rem;
  row-gap: 0;
  padding: 0 var(--padding-x);
  align-content: start;
  position: relative;
  z-index: 3;
}
@media (min-width: 768px) {
  .collaborations_content_grid {
    gap: 1.6rem;
    row-gap: 1.6rem;
    grid-template-columns: repeat(4, 1fr);
  }
}
@media (min-width: 1200px) {
  .collaborations_content_grid {
    display: none;
  }
}
.collaborations_content_grid_item {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.collaborations_content_grid_item .collaborations_content_grid_item_logo {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  margin: 0 auto;
}
.collaborations_content_grid_item .collaborations_content_grid_item_logo img {
  object-fit: contain;
  width: 100%;
  height: 100%;
}

/* ------- rotating arc of names (desktop) ------- */
.collaborations_content_arc {
  display: none;
  pointer-events: none;
  z-index: 1;
}
@media (min-width: 1200px) {
  .collaborations_content_arc {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
.collaborations_content_arc_item {
  position: absolute;
  left: 23.2vw;
  top: 50%;
  --rel-index: calc(var(--index) - var(--progress, 0) * (var(--total-items) - 1));
  --abs-rel-index: max(var(--rel-index), calc(-1 * var(--rel-index)));
  --radius: 58rem;
  --theta: calc(var(--rel-index) * 11deg);
  --x: calc(var(--radius) * (1 - cos(var(--theta))));
  --y: calc(var(--radius) * sin(var(--theta)));
  transform: translate(calc(-1 * var(--x)), calc(-50% + var(--y))) rotate(calc(var(--theta)));
  transform-origin: center left;
  color: rgba(255, 253, 226, 0.2);
  font: 600 8.8rem/1em var(--font-twk);
  letter-spacing: -0.03rem;
  letter-spacing: -0.264rem;
  transition: color 0.4s var(--easeOutQuart);
}
.collaborations_content_arc_item,
.collaborations_content_arc_item * {
  white-space: nowrap;
  text-wrap: nowrap;
  width: max-content;
}
.collaborations_content_arc_item .line,
.collaborations_content_arc_item .word {
  display: inline-block;
  white-space: nowrap !important;
  text-wrap: nowrap !important;
  width: max-content !important;
}
.collaborations_content_arc_item.active {
  color: var(--brand-yellow);
}

/* ------- logo stack (desktop) ------- */
.collaborations_content_logos {
  display: none;
}
@media (min-width: 1200px) {
  .collaborations_content_logos {
    display: block;
    width: 23.7rem;
    height: 23.7rem;
    position: absolute;
    top: 50%;
    left: 110.04rem;
    transform: translateY(-50%);
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    place-content: center;
    place-items: center;
    z-index: 2;
  }
  .collaborations_content_logos > * {
    grid-column: 1/-1;
    grid-row: 1/-1;
  }
}
.collaborations_content_logos_item {
  position: relative;
  width: 100%;
  height: 100%;
}
.collaborations_content_logos_item img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  -webkit-user-drag: none;
}
````

## Source — script.js
````javascript
gsap.registerPlugin(ScrollTrigger);

const section = document.querySelector("#collaborations");
const content = document.querySelector(".collaborations_content");
const arcItems = gsap.utils.toArray(".collaborations_content_arc_item");
const logoItems = gsap.utils.toArray(".collaborations_content_logos_item");
const gridItems = gsap.utils.toArray(".collaborations_content_grid_item");
const description = document.querySelector(".collaborations_content_description p");
const TOTAL = arcItems.length;
const LAST = TOTAL - 1;

/* ---------- split each brand name into words (like the original SplitText) ---------- */
arcItems.forEach((item) => {
  item.__words = splitWords(item);
});

function splitWords(el) {
  const text = el.textContent;
  el.textContent = "";
  const words = [];
  text.trim().split(/\s+/).forEach((w, i, arr) => {
    const span = document.createElement("span");
    span.className = "word";
    span.textContent = w;
    el.appendChild(span);
    words.push(span);
    if (i < arr.length - 1) {
      el.appendChild(document.createTextNode(" "));
    }
  });
  return words;
}

/* ---------- smooth scroll (Lenis, as on the original site) ---------- */
if (window.Lenis) {
  const lenis = new Lenis({ lerp: 0.12, smoothWheel: true });
  lenis.on("scroll", ScrollTrigger.update);
  gsap.ticker.add((time) => lenis.raf(time * 1000));
  gsap.ticker.lagSmoothing(0);
}

const mm = gsap.matchMedia();

/* ================= DESKTOP (>=1200px): wheel + logo stack ================= */
mm.add("(min-width: 1200px)", () => {
  let currentIndex = 0;
  let lastProgress = null;
  const logoTweens = new Map();

  /* initial hidden states */
  arcItems.forEach((item) => {
    gsap.set(item.__words, { opacity: 0, filter: "blur(10px)" });
  });
  logoItems.forEach((el) => {
    gsap.set(el, { opacity: 0, filter: "blur(10px)", visibility: "hidden" });
  });

  /* entrance: words of the arc, staggered per item, on section in-view */
  arcItems.forEach((item, i) => {
    gsap.to(item.__words, {
      opacity: 1,
      filter: "blur(0px)",
      duration: 1.4,
      ease: "power3.out",
      stagger: 0.05,
      delay: 0.1 * i,
      scrollTrigger: {
        trigger: content,
        start: "top bottom-=33.33%",
        once: true,
      },
    });
  });

  /* entrance: first logo */
  gsap.to(logoItems[0], {
    opacity: 1,
    filter: "blur(0px)",
    duration: 1.4,
    ease: "power3.out",
    onStart: () => gsap.set(logoItems[0], { visibility: "visible" }),
    scrollTrigger: {
      trigger: content,
      start: "top bottom-=33.33%",
      once: true,
    },
  });

  /* state updates driven by the scroll position */
  const setActive = (idx) => {
    arcItems.forEach((el, i) => el.classList.toggle("active", i <= idx));
  };
  const setLogo = (idx) => {
    logoItems.forEach((el, i) => {
      const prev = logoTweens.get(el);
      if (prev) prev.kill();
      let t;
      if (i === idx) {
        t = gsap.fromTo(
          el,
          { opacity: 0, filter: "blur(10px)", visibility: "visible" },
          { opacity: 1, filter: "blur(0px)", duration: 1.4 * 1.3, ease: "power3.out" }
        );
      } else {
        t = gsap.to(el, {
          opacity: 0,
          filter: "blur(10px)",
          duration: 1.4,
          ease: "power3.out",
          onComplete: () => gsap.set(el, { visibility: "hidden" }),
        });
      }
      logoTweens.set(el, t);
    });
  };

  /* the pinned scrub: section (300svh) with sticky content (100svh) */
  const scrub = ScrollTrigger.create({
    trigger: section,
    start: "top top",
    end: "bottom bottom",
    scrub: true,
    invalidateOnRefresh: true,
    onUpdate: (self) => {
      const p = Math.round(self.progress * 1e4) / 1e4;
      if (p !== lastProgress) {
        lastProgress = p;
        content.style.setProperty("--progress", String(p));
      }
      const idx = Math.floor(p * LAST);
      if (idx !== currentIndex) {
        currentIndex = idx;
        setActive(idx);
        setLogo(idx);
      }
    },
  });

  return () => {
    scrub.kill();
    logoTweens.forEach((t) => t.kill());
    arcItems.forEach((item) => gsap.set(item.__words, { clearProps: "all" }));
    logoItems.forEach((el) => gsap.set(el, { clearProps: "all" }));
  };
});

/* ================= MOBILE / TABLET (<1200px): logo grid ================= */
mm.add("(max-width: 1199.98px)", () => {
  let played = false;

  gsap.set(gridItems, { opacity: 0 });

  const play = () => {
    if (played) return;
    played = true;
    gsap.to(gridItems, {
      opacity: 1,
      duration: 0.6,
      stagger: 0.025,
      ease: "power2.out",
    });
  };

  const st = ScrollTrigger.create({
    trigger: content,
    start: "top+=30% bottom",
    once: true,
    onEnter: play,
  });

  /* if the section is already in view when set up, play immediately */
  if (content.getBoundingClientRect().top < window.innerHeight) {
    st.kill();
    play();
  }

  /* description reveal (only on mobile/tablet, as in the original) */
  const descTween = gsap.fromTo(
    description,
    { opacity: 0, filter: "blur(10px)", y: "1.6rem" },
    {
      opacity: 1,
      filter: "blur(0px)",
      y: 0,
      duration: 1.4,
      ease: "power3.out",
      scrollTrigger: {
        trigger: content,
        start: "top bottom-=33.33%",
        once: true,
      },
    }
  );

  return () => {
    st.kill();
    descTween.scrollTrigger && descTween.scrollTrigger.kill();
    gsap.set(gridItems, { clearProps: "all" });
    gsap.set(description, { clearProps: "all" });
  };
});
````
