- GSAP điều phối toàn bộ animation từ WebGL shader tới CSS clip-path wipes qua một số progress duy nhất từ 0 đến 1.
- Portfolio chuyển từ Three.js sang OGL để giảm bundle, dùng persistent WebGL context mount một lần tại page root tránh memory leak.
- Idle guard tự suspend flowmap render sau 90 frame không có input, direct DOM mutation thay React state đạt 120 Hz mượt mà.
- Awwwards Honorable Mention tháng 3/2026.
TL;DR

Thibault Guignand vừa chia sẻ case study chi tiết trên Codrops về cách anh xây dựng lại portfolio cá nhân - nơi mọi animation từ WebGL shader cho tới CSS clip-path đều được điều phối bởi một hệ thống motion duy nhất chạy trên GSAP. Chìa khoá là một số progress từ 0 đến 1 làm cầu nối giữa JavaScript và GLSL. Portfolio đạt Awwwards Honorable Mention vào tháng 3/2026 và nhận điểm cao nhất 9.2 từ ban giám khảo.
Vấn đề cần giải quyết
Khi bạn kết hợp WebGL shader, CSS transition, và scroll interaction trong cùng một trang, vấn đề kinh điển xuất hiện: mỗi layer chạy theo đồng hồ riêng. Shader có render loop của Three.js/OGL. CSS có timeline của trình duyệt. GSAP có ticker của mình. Kết quả? Animation lệch pha, jank ở 60 Hz, và code maintenance trở thành ác mộng.
Guignand giải quyết điều này bằng cách biến GSAP thành nguồn sự thật duy nhất - mọi thứ đều đồng bộ qua gsap.ticker.add(), và mọi hiệu ứng đều phản ứng với cùng một progress value.
Kiến trúc: một số, toàn bộ animation
Nguyên lý cốt lõi: "The entire choreography is driven by a single progress number between 0 and 1."
Con số này đóng vai trò uniform trong GLSL shader - GSAP animate nó từ 0 đến 1 thông qua gsap.to(material.uniforms.uProgress, { value: 1, duration: 1.5, ease: 'power2.inOut' }), và shader đọc giá trị này để tính toán mọi hiệu ứng hình ảnh. Không cần callback phức tạp, không cần event bridge.
Để GPU và JS không lệch phase, renderer đồng bộ trực tiếp với GSAP ticker:
gsap.ticker.add(stage.render.bind(stage))
Mỗi frame, GPU upload chỉ xảy ra với texture đang active trong transition - tiết kiệm tài nguyên đáng kể so với naive approach thông thường.
Kỹ thuật đằng sau từng hiệu ứng
GSAP plugins được dùng:
SplitText- chia text thành từng character/line để reveal tuần tự với staggerScrollTriggervớiscrub: 1- scroll interaction mượt mà, liên kết trực tiếp với vị trí scroll- Timeline API - choreograph nhiều animation stage theo trình tự rõ ràng
- Custom easing functions drive trực tiếp shader uniform, không qua CSS
Shader effects:
- Block-reveal mask - pixelated noise sampling tạo hiệu ứng reveal theo khối pixel
- Displacement warping - parabolic easing curves tạo cảm giác distort vật lý
- Chromatic aberration - RGB channel offset bất đối xứng, trở thành visual signature nhận diện portfolio
- Flowmap cursor distortion - velocity-driven rainbow effect theo chuyển động chuột
Page navigation: Kết hợp View Transitions API với GSAP fadeout để có full control, không phụ thuộc vào browser animation timing.
Text reveal đặc biệt: Scramble effect và clip-path wipe chạy song song - mask chỉ mở ra trên phần text đã legible, tránh visual noise khi chưa decode xong.
Hiệu năng thực chiến
Một số quyết định kỹ thuật giúp portfolio chạy 120 Hz mượt mà:
- OGL thay Three.js - chuyển đổi giữa chừng để có bundle nhẹ hơn và API đơn giản hơn cho use case này
- Persistent WebGL context - mount một lần ở page root, chỉ swap texture qua props thay vì remount component - loại bỏ hoàn toàn memory leak và stuttering
- Direct DOM mutations - dùng
element.style.*thay React state cho animation, tránh re-render không cần thiết ở mỗi frame - Idle guards - flowmap render loop tự suspend sau 90 frame liên tục không có input từ người dùng
- Build-time metadata -
image-dimensions.jsonvà base64 blur-up được pre-generate lúc build, loại bỏ layout shift hoàn toàn - Preload race pattern - data fetch và asset preload kích hoạt trước khi GSAP animation bắt đầu, giảm perceived load time đáng kể
Scroll auto-navigation: state machine 3 trạng thái
Auto-scroll navigation dùng state machine: idle - triggered - navigating, với velocity ceiling và progress-history checks để tránh trigger không mong muốn. Đây là chi tiết thường bị bỏ qua trong các tutorial thông thường - không phải cứ scroll qua threshold là navigate ngay, mà cần kiểm tra velocity và history để phân biệt intentional scroll với accidental gesture.
Ai nên đọc ngay
Case study này phù hợp nếu bạn là:
- Creative developer muốn xây portfolio "biết nói chuyện" kỹ thuật với hiring manager
- Frontend dev đang tìm cách kết hợp WebGL và GSAP mà không bị jank hay memory leak
- Đang dùng Three.js thấy bundle quá nặng - OGL là lựa chọn đáng cân nhắc thay thế
- Muốn hiểu architecture "single source of truth" cho animation system phức tạp
Nguồn & đọc thêm
Case study đầy đủ trên Codrops. Portfolio của Thibault Guignand: Awwwards Honorable Mention. Bài liên quan: How to Animate WebGL Shaders with GSAP.
