A cinematic, scroll-driven developer portfolio built with React + GSAP
Not your average portfolio. This is a frame-by-frame canvas animation engine wrapped around a full developer showcase — every scroll, reveal, and transition is intentional.
300 canvas frames · clip-path reveals · 3D flip cards
scroll-triggered text · elastic SVG line · custom cursor
sticky parallax cards · cinematic preloader · footer reveal
| Section | Highlight |
|---|---|
| Hero | 300-frame canvas scroll animation + clip-path reveal |
| About | SVG stroke-to-fill text + scroll-driven gradient fill |
| What I Do | Three-panel slide-in with scale collapse |
| Skills | Horizontal scroll marquee + stacked sticky cards |
| Approach | 3D flip cards with scroll-based gap & rotation |
| Education | Clip-path image reveals with per-char stagger |
| Projects | Pinned scroll with image wipe transitions |
| Experience | Horizontal parallax carousel |
| Contact/Footer | Full-section animated reveal with split text |
Core
- React 18 + Vite
- GSAP — SplitText · ScrollTrigger · CustomEase
- Lenis — smooth scroll synced with GSAP ticker
- Tailwind CSS v4
- Remixicon
Animation Techniques
clip-pathpolygon transitions for image reveals- Canvas
drawImagefor frame-by-frame scrubbing SplitTextwith mask for char/word staggerScrollTriggerpinning withscrubfor parallaxIntersectionObserverfor carousel title transitions- CSS custom properties driven by GSAP for overlay effects
# Clone the repo
git clone https://github.com/SuryaX2/portfolio.git
cd portfolio
# Install dependencies
npm install
# Start dev server
npm run devNote: The canvas animation requires 300 JPEG frames in
/public/Frames/namedframe_0001.jpeg→frame_0300.jpeg. These are not included in the repo due to file size.
src/
├── components/
│ ├── layout/
│ │ ├── Navbar.jsx # Full-screen animated menu
│ │ ├── HeroFooter.jsx # Animated hero bottom bar
│ │ ├── CustomCursor.jsx # GSAP-driven custom cursor
│ │ ├── SvgLine.jsx # Elastic mouse-follow SVG
│ │ └── Footer/
│ │ └── FooterReveal.jsx # Scroll-triggered footer
│ └── sections/
│ ├── Hero.jsx # Canvas frame player + preloader
│ ├── About.jsx # SVG text + gradient fill
│ ├── Skills.jsx # Marquee + sticky cards
│ ├── FlipCards/ # 3D scroll-driven flip cards
│ ├── Educational-Section/ # Clip-path image scroll
│ ├── ProjectSection/ # Pinned project scroll
│ └── ExpCarousel/ # Horizontal parallax carousel
├── hooks/
│ └── useLenis.js # Lenis + GSAP ticker setup
├── context/
│ └── LenisContext.js # Shared scroll instance
└── pages/
└── Home.jsx
Frame scrubbing on canvas
ScrollTrigger.create({
trigger: heroRef.current,
start: "top top",
end: "+=300%",
scrub: 2,
onUpdate: (self) => {
const target = Math.round(self.progress * (FRAME_COUNT - 1));
drawFrame(target);
},
});Clip-path reveal
tl.to(heroBgRef.current, {
clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
duration: 2,
ease: "hop",
});SplitText with mask
const split = SplitText.create(el, { type: "chars", mask: "chars" });
gsap.from(split.chars, { y: "100%", stagger: 0.05, ease: "power4.out" });- All frames are preloaded before scroll is unlocked
ResizeObserverkeeps canvas dimensions in syncwillChange: transformapplied to animated elements- Lenis scroll is paused during preloader, menu open states
ScrollTrigger.refresh()called after pinned sections are ready
MIT — feel free to use this as reference or inspiration. A star ⭐ is appreciated if it helped you!
Designed & Built by Surya Sekhar Sharma
React · GSAP · Lenis · Tailwind · Vite
