Overview
A cinematic single-page portfolio built for a visual effects context. The goal: make the browser feel like a 3D environment, not a webpage. Every scroll event is a deliberate camera move. Every hover is a material response.
Built with React Three Fiber, GSAP ScrollTrigger, Lenis, and hand-written GLSL shaders.
What was built
Kinetic headline — GSAP ScrollTrigger linked headline: letter-spacing expands, skew rotates, words stagger in/out as you scroll. No layout triggers — transform and opacity only.
Stacking project cards — GSAP ScrollTrigger pin + overlap. Each project card slides under the next, creating a physical stacking feel. The scroll scrub is synced to Lenis for perfect smooth physics.
Project planes — Per-project R3F planes with a custom wave + RGB-split shader. Wave amplitude responds to mouse proximity. RGB channels separate on hover for a chromatic aberration effect.
Lenis + GSAP sync — Lenis drives the GSAP ticker directly via gsap.ticker.add. Every scroll effect is synced to the same RAF — no jitter between the particle field and the GSAP animations.
Stack
| Layer | Technology |
|---|---|
| Build | Vite + React |
| 3D | React Three Fiber + Drei |
| Shaders | GLSL (custom vertex + fragment) |
| Animation | GSAP + ScrollTrigger |
| Scroll | Lenis |
| Styling | Tailwind CSS |
| Deploy | Vercel (Vite preset) |
Performance notes
dpr={[1, 2]}caps DPR at 2× for retina without thrashing lower-end GPUs.- All scroll effects are transform + opacity only. Zero layout triggers.
- The grain overlay is a
fixed inset-0canvas — never attached to a scroll container.
Key decisions
Why R3F over Three.js direct? React component model makes scene management dramatically simpler. useFrame, useThree, and Drei utilities cut boilerplate to near-zero while keeping full Three.js escape hatches.
Why Lenis over native scroll? Consistent scroll physics across browsers. The ease curve is controllable — the portfolio needed a specific "heavy" feel, not the snappy default browser scroll.
Why Vite not Next.js? No SSR needed for a purely visual portfolio. Vite's HMR is faster for shader iteration — a critical feedback loop when tuning GLSL.





