Smooth Scroll

Lesson 16

The content wrapper is position: fixed so it doesn’t scroll naturally. A .scroll-spacer div is sized to the wrapper’s scrollHeight, giving the page its scrollable distance. gsap.ticker.add() runs every animation frame: current += (window.scrollY - current) * 0.08 lerps the internal position, then gsap.set(wrapper, { y: -current }) applies it. Lower ease = dreamier, higher ease = snappier.

Source Code script.js
const wrapper = document.querySelector('.wrapper');
const spacer = document.querySelector('.scroll-spacer');

// Size the spacer to match the fixed wrapper so the page is scrollable
function resize() {
  spacer.style.height = wrapper.scrollHeight + 'px';
}
resize();
window.addEventListener('resize', resize);

// Lerp current scroll position toward native scroll target each frame
let current = 0;
const ease = 0.08;

gsap.ticker.add(() => {
  const target = window.scrollY;
  current += (target - current) * ease;
  gsap.set(wrapper, { y: -current });
});