webdevpuneet.com

Scroll-Pinned Story — Free GSAP ScrollTrigger Snippet

Scroll-Pinned Mountain Story · Scroll · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="wrapper">
  <div class="intro">
    <h1>The Path Through</h1>
    <p>the silent mountains</p>
    <div class="scroll-cue" aria-hidden="true">
      <span>Scroll to begin</span>
      <img class="scroll-cue-icon" src="/images/ui-snippets/scroll-pin-story/scroll-cue-mouse.svg" alt="">
    </div>
  </div>
  <div class="content">
    <section class="section hero"></section>
    <section class="section gradient-purple"></section>
    <section class="section gradient-blue">
      <div class="test">
        <p>Beyond the last village, the trail climbs into mist and stone, where no map dares to follow.</p>
        <p>Here the wind carries old songs, and the peaks keep their secrets beneath centuries of snow.</p>
        <p>Press onward, traveler — for what waits at the summit has been waiting far longer than memory itself.</p>
      </div>
    </section>
  </div>
  <div class="image-container">
    <img src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3" alt="Misty mountain trail winding through the peaks">
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

GSAP timeline pinned with ScrollTrigger (`pin: true, scrub: true`) — the animation plays in lockstep with the scrollbar, forward and backward, instead of just firing once
Parallel zoom tweens chained with the "<" position parameter — the hero photo and background section scale together for a layered, parallax-style depth effect
Pin releases the instant the zoom reaches max scale, so the story below comes up scrolling immediately rather than holding the visitor on a static frame
Independent ScrollTrigger on the story text with toggleActions: "play none none reverse" — paragraphs fade and rise into view with a stagger as they cross the viewport, and reverse cleanly on scroll-up
Self-contained SVG scroll cue with internal @keyframes — animates without depending on any external CSS, and toggles on window.scrollY so it reappears when the visitor scrolls back to the top
jQuery darkenScroll helper computes scroll progress with .scrollTop()/.offset() and applies it as an inset box-shadow to vignette the hero image — a small effect that is awkward to write as a pure GSAP tween
All three dependencies (GSAP, ScrollTrigger, jQuery) load from CDN URLs — paste the HTML, CSS, and JS into any page and it runs with no bundler, npm install, or build step

About this UI Snippet

How this scroll-pinned story animation was built — GSAP, ScrollTrigger, and jQuery

Screenshot of the Scroll-Pinned Mountain Story snippet rendered live

This snippet recreates the "scrollytelling" effect you see on travel features, product launch pages, and Awwwards-winning portfolios — the page locks in place while a hero photo zooms in, then releases into a story that fades into view as you keep scrolling. It's built entirely with GSAP (the GreenSock Animation Platform), the ScrollTrigger plugin, and a small bit of jQuery for one supporting effect — three scripts loaded straight from a CDN, no bundler or build step required.

The pin-and-scrub timeline

The core trick is a single GSAP timeline wired to a ScrollTrigger with pin: true and scrub: true. Pinning freezes the .wrapper element in place for a fixed scroll distance, and scrubbing ties the timeline's playhead directly to the scrollbar — scroll down and the animation plays forward, scroll up and it reverses, frame for frame. Inside that pinned window, two tweens run in parallel (chained with GSAP's "<" position parameter): the hero photo scales up to 2x with a z translation for depth, while the background section scales to 1.4x for a subtle parallax push. The moment the zoom hits its max scale, the pin releases — so the story content comes up scrolling immediately, instead of leaving the visitor stuck on a frozen frame waiting for something to happen. If you'd rather build this kind of timeline up from scratch and see each property change live, the GSAP Playground walks through pin, scrub, and timeline mechanics with a guided live editor and instant preview.

Staggered text reveal on scroll

Once the pin lets go, the page scrolls normally and the story paragraphs animate in with their own gsap.from() call, stagger: 0.3, and toggleActions: "play none none reverse" on a dedicated ScrollTrigger — each line rises and fades in just as it crosses into the viewport, and reverses cleanly if the visitor scrolls back up. It's the same staggered-reveal idea behind our Reveal on Scroll snippet, just pointed at one specific block instead of the whole page.

Supporting touches

A small custom SVG — a minimalist "scroll to begin" mouse icon with its own internal @keyframes animation — nudges first-time visitors to start scrolling, in the same spirit as the indicator in our Scroll Progress Bar snippet, and toggles itself back on whenever the visitor returns to the top. At the bottom of the JS, a short jQuery helper darkens the hero photo as the visitor scrolls past it — computing scroll progress with .scrollTop() and .offset() and applying it as an inset box-shadow, a small effect that's awkward to express with GSAP tweens alone, which is why jQuery still earns a spot in the CDN list here.

Step by step

How to Use

  1. 1
    Lay out the three layersStructure the markup as an .intro title overlay, a .content block of stacked sections (hero, gradient spacers, story text), and an absolutely-positioned .image-container that sits on top and gets zoomed.
  2. 2
    Load GSAP, ScrollTrigger, and jQuery from a CDNAdd the three script URLs — GSAP core, the ScrollTrigger plugin, and jQuery — then call gsap.registerPlugin(ScrollTrigger) before building any timelines.
  3. 3
    Pin the wrapper and scrub a zoom timelineCreate a gsap.timeline() with scrollTrigger: { trigger: ".wrapper", pin: true, scrub: true, end: "+=50%" }, then .to() the image and hero section to scale up in parallel using the "<" position parameter.
  4. 4
    Reveal the story as it scrolls into viewAdd a second, independent ScrollTrigger on the .test text block with start: "top 85%" and toggleActions: "play none none reverse", animating the paragraphs from() opacity: 0, y: 40 with a stagger.
  5. 5
    Add the scroll cue and darken effectDrop in a small SVG icon with its own CSS @keyframes, toggle its visibility based on window.scrollY, and finish with the jQuery darkenScroll helper that dims the hero photo as it scrolls past.
  6. 6
    Copy the code and swap the contentUse the Copy buttons to grab the HTML, CSS, and JS, then replace the title, story paragraphs, and image URLs with your own — the timeline and triggers keep working as long as the class names stay aligned.

Real-world uses

Common Use Cases

Travel, editorial, and long-form storytelling pages
The pin-zoom-reveal sequence is the backbone of magazine-style scroll features — a hero photo draws the eye in, then the narrative unfolds paragraph by paragraph as the visitor keeps scrolling.
Product launch and portfolio hero sections
Recreate the cinematic "zoom into the photo, then reveal the pitch" intros seen on Awwwards sites and agency portfolios, adapted to your own photography and copy.
Learning how ScrollTrigger pin and scrub fit together
The timeline here is a compact, real-world example of pinning, scrubbing, and chained position parameters — far easier to study line by line than a sprawling production codebase.
A starting point for your own scrollytelling sections
Copy the HTML, CSS, and JS as a base, then swap in your own images, headline, and story copy — the ScrollTrigger setup keeps working as long as the class names line up with the JS selectors.
A reference for staggered scroll-reveal text
The story-text trigger doubles as a clean, copyable example of gsap.from() with stagger and toggleActions — the exact pattern to reach for any time you need text or cards to fade in as the visitor scrolls.
A practical example of pairing GSAP with jQuery and SVG
Shows a real case for keeping a small jQuery helper alongside GSAP, plus a self-animating inline SVG icon — handy if you maintain an older codebase or want ideas for your own SVG Motion Studio animations.
Related: Scroll-Snap Peek Carousel
See the Scroll-Snap Peek Carousel for a related scroll pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Wrap the section you want to "freeze" in a container, then give a GSAP timeline a scrollTrigger with trigger, pin: true, and scrub: true. While the visitor scrolls through the pinned distance (set with end, e.g. "+=50%"), the timeline's playhead tracks the scroll position directly — so your tweens (zooms, fades, moves) play forward and backward in sync with the scrollbar. That is the entire mechanism behind this snippet's zoom-in intro.

scrub: true ties the animation's progress directly to the scrollbar position — scroll halfway through the trigger's range and the timeline is exactly halfway done, and scrolling back up reverses it instantly, frame for frame. A normal ScrollTrigger, like the one used for this snippet's story-text reveal with toggleActions: "play none none reverse", instead plays the animation once when an element enters the viewport and reverses it as a whole when it leaves — it does not track scroll position moment to moment.

The ScrollTrigger's end is set to roughly match the zoom timeline's own duration (end: "+=50%"), so the pinned scroll distance ends right when the last tween completes. That keeps the experience snappy — the moment the zoom maxes out, the pin lets go and the story content scrolls up immediately, with no "dead" stretch of scrolling where nothing visibly changes.

No — GSAP and ScrollTrigger work entirely on their own and have no dependency on jQuery. This snippet only loads jQuery for one small supporting effect: darkening the hero photo based on scroll position via .scrollTop() and .offset(). If you do not need that vignette effect, you can drop the jQuery <script> tag and the darkenScroll block entirely — the pin, zoom, and text-reveal animations keep working unchanged.

Give the text element its own ScrollTrigger — separate from any pinned timeline — with a start like "top 85%" and toggleActions: "play none none reverse". Animate it with gsap.from(selector, { opacity: 0, y: 40, stagger: 0.3, scrollTrigger: {...} }); GSAP plays the tween forward when the element enters the trigger zone and reverses it when it scrolls back out. This exact pattern drives the staggered paragraph reveal in this snippet.

Yes — copy the HTML, CSS, and JS with the buttons on this page and adapt them freely, including in commercial projects. GSAP's core library and the ScrollTrigger plugin used here are free under the GreenSock license; only the separate Club GreenSock plugins (like SplitText or MorphSVG, neither of which is used in this snippet) require a paid membership. Swap in your own images, headline, and story text, and the animation keeps working as long as the element class names stay aligned with the JS selectors.