Three.js + GSAP ScrollTrigger + Video Performance Issue on Low-End Devices

Hi everyone,

I’m working on a parallax scrolling page using GSAP ScrollTrigger and Three.js, but I’m facing performance issues on low-end devices, particularly with video playback stuttering. I’ve applied some optimizations, but the page still feels heavy, so I’m looking for additional solutions.

:pushpin: Current Implementation

  1. Parallax Scrolling: Implemented smooth scrolling with GSAP ScrollTrigger
  2. Full-Width Video Section: At the beginning of the page, I have a <video> tag set to autoplay and loop
  3. Three.js Canvas: Added a WebGL scene in the middle of the page
  4. Performance Issues on Low-End Devices:
  • Video playback stutters and lags (especially on mobile/low-end PCs)
  • Overall page performance is degraded when multiple animations run

:hammer_and_wrench: Optimizations I Have Applied

  1. ScrollTrigger-based Three.js requestAnimationFrame() Control
  • requestAnimationFrame() only runs when the WebGL section is visible
  • Previously, requestAnimationFrame() was running every frame, even when Three.js was off-screen
  1. Video stuttering issue improved
  • Limiting animate() execution reduced CPU load
  1. :x: Remaining Issue: The page still feels heavy
  • Performance drops when GSAP animations and Three.js render together
  • Even when WebGL is inactive, animations feel sluggish

:question: Questions

  • Has anyone faced a similar issue before?
  • What additional optimizations can I apply?
    • Should I completely disable the Three.js canvas (display: none) when it’s off-screen?
    • Any WebGL rendering optimizations that can help?
    • Any video playback performance tricks that reduce CPU/GPU load?

I’m particularly looking for advice from people who have worked with GSAP ScrollTrigger + Three.js + HTML5 Video and encountered performance bottlenecks.

Thanks in advance for any insights! :pray:

:bulb: Tech Stack: Three.js, GSAP (ScrollTrigger), HTML5 Video

You don’t need to use scrollTrigger to check that webGL canvas is visible. You can only check its boundinClientRect every animation frame and skip it if canvas is out of the viewport:

const {top, bottom} = canvas.getBoundingClientRect();
if (bottom < 0 || top > window.innerHeight) return;