webdevpuneet.com

ScrollSmoother Parallax Page — Free GSAP Snippet

ScrollSmoother Parallax Page · Scroll · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div id="smooth-wrapper">
  <div id="smooth-content">
    <section class="ssp-hero">
      <h1 data-speed="0.85">Butter-smooth scrolling</h1>
      <p data-speed="0.92">This whole page is riding ScrollSmoother. Speeds differ per element — that's the parallax.</p>
    </section>
    <section class="ssp-row">
      <div class="ssp-card" data-speed="1.08" style="--cc:#6366f1"><b>1.08×</b><span>faster than scroll</span></div>
      <div class="ssp-card" data-speed="1.0" style="--cc:#0ea5e9"><b>1.0×</b><span>normal speed</span></div>
      <div class="ssp-card" data-speed="0.9" style="--cc:#a855f7"><b>0.9×</b><span>slower than scroll</span></div>
    </section>
    <section class="ssp-band">
      <h2 data-speed="0.95">Lag makes things feel attached by springs</h2>
      <div class="ssp-chips">
        <span class="ssp-chip" data-lag="0.1">lag 0.1</span>
        <span class="ssp-chip" data-lag="0.3">lag 0.3</span>
        <span class="ssp-chip" data-lag="0.6">lag 0.6</span>
      </div>
    </section>
    <section class="ssp-end"><p>Native scrollbar, synthetic smoothness.</p></section>
  </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

Native scrollbar kept
Real scrolling; only rendering is smoothed.
Transform-based glide
Content translates to catch up over 1.2s.
Markup parallax
data-speed multiples, zero JavaScript per element.
Center-compensated
Elements sit naturally when viewport-centered.
Spring lag
data-lag adds per-element settle delays.
Touch-respectful
smoothTouch keeps mobile momentum native.
ScrollTrigger-native
Pins and scrubs compose without changes.
A11y preserved
Keyboard, anchors, find-in-page still work.

About this UI Snippet

ScrollSmoother Parallax — Smooth Scrolling With the Native Scrollbar Intact

Screenshot of the ScrollSmoother Parallax Page snippet rendered live

"Smooth scrolling" libraries traditionally hijack the wheel, fake a scrollbar, and break accessibility. GSAP's ScrollSmoother (free on the CDN since 3.13) takes the opposite approach: the browser scrolls natively — real scrollbar, keyboard, find-in-page, anchor links all intact — while the visible content is *translated* to catch up smoothly. This snippet wires a whole page through it, with per-element data-speed parallax and data-lag spring effects declared entirely in markup.

How the wrapper/content trick works

ScrollSmoother requires exactly two elements: #smooth-wrapper (which it fixes in place) and #smooth-content (the actual page, which it moves with transform: translateY). The document retains its full natural height, so the native scrollbar reflects real position — but instead of the browser jumping content per scroll event, ScrollSmoother eases the transform toward the scroll position over smooth: 1.2 seconds. Scroll hard and content glides after the scrollbar like it has mass. Because it's transform-based, everything stays compositor-friendly.

data-speed is declarative parallax

With effects: true, any element carrying data-speed moves at that multiple of scroll speed: 0.85 drifts slower than the page (appearing deeper), 1.08 outruns it (appearing closer). The three demo cards sit side by side with 1.08 / 1.0 / 0.9 — scroll and watch them shear apart, a controlled experiment in perceived depth. Crucially, ScrollSmoother auto-compensates so each element is exactly at its natural position when it's centered in the viewport; parallax offsets grow toward the viewport edges. That's why speeds don't wreck layouts the way hand-rolled parallax does.

data-lag is parallax's springy cousin

Where speed changes an element's velocity, data-lag delays it: a lag of 0.3 means the element takes an extra 0.3 seconds to catch up to where it should be, then settles. The chip row (lags 0.1 / 0.3 / 0.6) turns a scroll stop into a little cascade of settling elements — the "attached by soft springs" feel that agencies charge for, in one attribute.

smoothTouch is deliberately tiny

Touch devices already have physical momentum scrolling; stacking 1.2s of synthetic smoothing on top feels drunk. smoothTouch: 0.1 applies just enough to keep effects working on mobile while respecting the platform's native feel — or set it false to disable entirely.

It's built on ScrollTrigger, and they compose

ScrollSmoother requires ScrollTrigger and registers itself as its scroller — meaning every ScrollTrigger technique (pins, scrubs, snap) works inside a smoothed page without changes. The pinned scroll effects across this library can drop into #smooth-content as-is.

What to watch out for

position: fixed elements must live *outside* #smooth-content (inside a transformed ancestor, fixed becomes absolute — a CSS spec behavior, not a plugin bug). And because content height is virtualized against the transform, dynamically injected content needs ScrollTrigger.refresh().

Customizing it

Tune smooth (0.8 subtle, 2 dramatic), spread data-speed/data-lag across real imagery, or add ScrollSmoother.create().effects() targets dynamically. Related: scrubbed depth in scroll parallax layers, row-shear in hero parallax grid, mouse parallax in parallax hero, and native-scroll pinning in scroll sticky features.

Step by step

How to Use

  1. 1
    Add the GSAP CDNsgsap, ScrollTrigger, and ScrollSmoother — order matters.
  2. 2
    Paste HTML, CSS, and JSThe wrapper/content structure is required exactly.
  3. 3
    Scroll the pageContent glides after the scrollbar with inertia.
  4. 4
    Watch the cardsdata-speed values shear them apart in depth.
  5. 5
    Stop scrolling abruptlydata-lag chips settle in a spring cascade.
  6. 6
    Add effects anywhereAny element takes data-speed or data-lag.

Real-world uses

Common Use Cases

Agency portfolios
Give a site the signature glide plus depth, combined with scroll parallax layers in individual sections.
Product marketing pages
Float screenshots at different speeds using data-speed, with the browser still scrolling natively and keeping its real scrollbar.
Editorial long reads
Add weight and lag to photo essays, with content translating to catch up over 1.2 seconds and a scroll reading time pill for orientation.
Layered hero depth
Build hero depth like a hero parallax grid, with elements compensating so they sit naturally when centred in the viewport.
Interactive resumes
Smooth single-page resumes with a scroll-to anchor nav, or combine with pointer depth from a parallax hero.
Related: Shrink on Scroll Header
See the Shrink on Scroll Header for a related scroll pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Philosophy: the browser keeps scrolling natively — real scrollbar, keyboard paging, anchor jumps, find-in-page — and ScrollSmoother only eases the content's transform toward the true scroll position. Hijack-style libraries intercept wheel events and synthesize scrolling, which breaks those native behaviors. It also registers itself with ScrollTrigger, so the entire pin/scrub ecosystem works unchanged.

The wrapper is fixed to the viewport (the window the user sees through) while the content element — the real page at its natural height — is translated within it. The document keeps its full height so the scrollbar stays honest, and the translate is what gets smoothed. Without that two-element split there'd be nothing to transform independently of the scroll position.

ScrollSmoother anchors every effect element to its natural position at the moment it's centered in the viewport — parallax offsets grow toward the edges and vanish at center. So a 0.9× image is exactly where CSS put it when the user looks straight at it, drifting only while entering and leaving. Hand-rolled parallax lacks that compensation, which is why it usually collides with neighbors.

Speed scales an element's scroll velocity (depth illusion); lag delays it — a data-lag of 0.3 lets the element fall behind its true position and spring back over 0.3 seconds when scrolling pauses. Speeds separate layers in space; lags separate them in time, producing that soft-spring settle when a fast scroll stops.

CSS spec: inside any transformed ancestor, fixed positioning resolves against that ancestor rather than the viewport — and #smooth-content is permanently transformed. Move genuinely fixed UI (navs, cookie bars, floating buttons) outside #smooth-content as siblings of the wrapper, where they'll behave normally while the page glides beneath them.

Create it in a mount effect — useEffect, onMounted, or ngAfterViewInit — after the wrapper/content DOM exists, and call smoother.kill() in the cleanup (plus ScrollTrigger.refresh() after route content changes). In SPAs, recreate per page or keep one instance at the layout level with content swapped inside. The required structure is two plain divs, so Tailwind handles all styling; keep fixed overlays outside the wrapper.

Yes — because the browser's real scroll position is what's driving everything, Page Down, arrow keys, Space, and Home/End all move the native scrollbar exactly as before, and ScrollSmoother just eases the visual transform to catch up. Screen readers that read document order and jump to headings or landmarks are unaffected since the DOM structure and focus order never change, only the transform applied for rendering.