More Scroll Snippets
Scroll Gallery Pin — Free GSAP ScrollTrigger Snippet
Scroll Gallery Pin · Scroll · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Scroll Gallery Pin — A Pinned Horizontal Gallery With Live Caption

Scroll gallery pin is the portfolio gallery where the section sticks to the screen and a row of images glides sideways as you scroll, with the heading and a counter updating to name whichever image is in focus — the polished showcase from agency and photography sites. This snippet builds it with GSAP and ScrollTrigger (from a CDN), plus plain HTML and CSS.
Pin and translate the rail
ScrollTrigger pins the gallery section and a single scrubbed tween moves the inner rail along its x-axis by its overflow distance (scrollWidth − innerWidth, plus a little padding so the last item clears the edge). So vertical scrolling drives the horizontal travel, and the section holds in place until the rail reaches its end — the standard horizontal-scroll-on-pin pattern, here applied to a captioned gallery.
A live caption and counter
What sets this apart from a plain horizontal scroller is the onUpdate callback. It reads self.progress (0 to 1), rounds it to the nearest item index, and writes that item's caption into the heading and updates a "03 / 05" counter. Because the index is rounded, the heading snaps to whichever image is most centered, giving the gallery a sense of discrete "slides" even though the motion is continuous. The counter uses tabular-nums so it doesn't jitter.
Responsive distance
The travel distance is computed in a function and invalidateOnRefresh: true re-runs it on resize, so the rail always scrolls exactly far enough regardless of viewport width — items are sized in vw so the gallery adapts, and the pin length (end) tracks the same function. This is what keeps a horizontal gallery from breaking or leaving dead scroll when the window changes.
Smooth catch-up
scrub: 1 gives the rail a one-second easing toward the scroll position, so the images glide with a touch of momentum rather than locking rigidly to the scrollbar — a more gallery-like feel. ease: 'none' on the tween keeps the underlying mapping linear while the scrub adds the smoothing.
Composited and clean
The rail moves with a transform (x), so it composites on the GPU with no reflow even with large image cards, and overflow: hidden on the pinned section clips the off-screen items. Captions sit over a gradient scrim so they stay legible on any image.
Customizing it
Swap the gradient figures for real images, change the item width or gap, add more items (the distance recalculates), or snap the rail to each item. Pair it with a scroll horizontal pin, a photo gallery, or a coverflow carousel.
Step by step
How to Use
- 1Add the GSAP CDNsInclude gsap and ScrollTrigger from the CDN panel.
- 2Paste HTML, CSS, and JSA pinned gallery with a heading renders.
- 3Scroll downThe image rail glides sideways.
- 4Watch the headingThe title and counter track the focused image.
- 5Resize the windowThe travel distance recalculates.
- 6Use real imagesSwap the gradient figures for photos.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
ScrollTrigger pins the section and a scrubbed tween moves the rail along x by its overflow distance (scrollWidth − innerWidth plus padding). Vertical scrolling drives the horizontal travel while the section holds in place until the rail ends — the horizontal-scroll-on-pin pattern, applied to a captioned image gallery.
The tween's onUpdate reads self.progress from 0 to 1, rounds it to the nearest item index, and writes that item's caption into the heading plus a 03 / 05 counter. Rounding makes the title snap to whichever image is most centered, giving discrete slide feedback even though the rail moves continuously.
Yes. The travel distance is a function and invalidateOnRefresh: true re-runs it on resize, so the rail scrolls exactly far enough at any width, and the pin length tracks the same function. Items are sized in vw so the gallery itself reflows, avoiding dead scroll or a cut-off last image.
scrub: 1 gives the rail a one-second easing toward the scroll position, so the images glide with a little momentum rather than locking rigidly to the scrollbar — a more gallery-like feel. ease: none keeps the underlying scroll-to-position mapping linear while the scrub value adds the smoothing on top.
In a mount effect, register ScrollTrigger and create the pinned, scrubbed tween scoped to refs for the section and rail, with onUpdate setting state (active index) or writing to refs for the caption and counter. Use function-based distance and invalidateOnRefresh. Return a cleanup that reverts the GSAP context. The CSS ports unchanged.