← tasteMarkdown

GSAP, Scroll Worlds and WebGL over HTML

Condensed from: greensock/gsap-skills (https://github.com/greensock/gsap-skills), oso95/scroll-world (https://github.com/oso95/scroll-world), DavidHDev/canvas-ui (https://github.com/DavidHDev/canvas-ui)

When GSAP

Core rules

Timelines

ScrollTrigger

// Pinned, scrubbed story section
const tl = gsap.timeline({
  defaults: { ease: "none" },
  scrollTrigger: { trigger: ".story", start: "top top", end: "+=2000", pin: true, scrub: 1 },
});
tl.from(".story h2", { yPercent: 40, autoAlpha: 0 })
  .to(".story .device", { rotationY: -18, scale: 1.1 }, "<")
  .to(".story .caption", { autoAlpha: 1 }, ">-0.2");

// Fake horizontal scroll: ease MUST be "none"
const track = document.querySelector(".track");
const dist = () => track.scrollWidth - window.innerWidth;
const h = gsap.to(track, {
  x: () => -dist(), ease: "none",
  scrollTrigger: { trigger: ".h-wrap", pin: true, scrub: 1, end: () => "+=" + dist(), invalidateOnRefresh: true },
});
gsap.from(".panel-2 img", { scale: 0.9, scrollTrigger: { containerAnimation: h, trigger: ".panel-2", start: "left center" } });

containerAnimation triggers cannot pin or snap.

React and frameworks

gsap.registerPlugin(useGSAP, ScrollTrigger); // outside the component
const root = useRef(null);
const { contextSafe } = useGSAP(() => {
  gsap.from(".item", { autoAlpha: 0, y: 16, stagger: 0.06 });
}, { scope: root, dependencies: [items.length], revertOnUpdate: true });
const onClick = contextSafe(() => gsap.to(".badge", { scale: 1.1 }));

Plugins worth knowing

Performance and common mistakes

Scroll World: brand to 3D scroll landing

Canvas UI: WebGL over live HTML

Read by your agent through get_guide("gsap-scroll") in the taste MCP.