← Blog
Vibrant, wavy abstract 3D lines against a colorful gradient background
Photo by BoliviaInteligente on Unsplash

Three.js and GSAP: the two libraries behind almost every award-site hero

September 17, 2026

Run enough reverse_engineer_webgl and reverse_engineer_animations calls against Awwwards-caliber sites and a pattern shows up fast: it's rarely one exotic framework doing everything. It's almost always Three.js handling the scene, and GSAP — usually with ScrollTrigger — deciding when anything in that scene is allowed to move.

They're not competing for the same job

Three.js draws pixels: geometry, materials, a camera, a render loop. It has no opinion about scroll position, easing curves, or sequencing — left alone it will happily render the same static frame forever. GSAP has no opinion about WebGL at all; it interpolates numbers over time against whatever curve you give it. The pairing works because neither one tries to do the other's job: GSAP owns when and how fast, Three.js owns what gets drawn. The bridge between them is almost always a handful of shader uniforms or object properties GSAP is allowed to write to every frame.

The pattern in practice

Scrubbed camera or uniform, driven by scroll, not autoplay — the exact shape we see over and over in captured heroes:

const uniforms = { uProgress: { value: 0 } };

gsap.to(uniforms.uProgress, {
  value: 1,
  ease: "none",
  scrollTrigger: {
    trigger: ".hero",
    start: "top top",
    end: "+=150%",
    scrub: 1.2,
  },
});

function frame() {
  material.uniforms.uProgress.value = uniforms.uProgress.value;
  renderer.render(scene, camera);
  requestAnimationFrame(frame);
}

scrub: 1.2 is doing more work than it looks like — it adds a small lag between scroll position and animation value, which is the difference between motion that feels physically connected to the scrollbar and motion that feels like it's fighting it. ease: "none" on the ScrollTrigger tween itself matters too: the easing captured sites use lives in the shader or the scrub value, not stacked on top of it.

Where it actually breaks

GSAP never touches the GPU. Every award-site "shader animation" you've seen is GSAP changing a JavaScript number on a schedule, and a shader reading that number on the next frame it renders. Once you see that split, the rest of the pattern reads as plumbing, not magic.

If you want the exact curve, scrub value, and shader parameters for a specific site instead of the general pattern, that's what reverse_engineer_animations and reverse_engineer_webgl are for — run them together against the same URL and you get both halves of the bridge.