Skip to content
VedantGupta19402Public

About

A cinematic, scroll-driven tattoo studio website that turns ink art into a premium visual storytelling experience using video, motion, and editorial UI design.

Resources

Stars

16 stars

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

Repository files navigation

ORION BLACK

Every mark is a decision. Every decision is permanent.

A high-end interactive tattoo studio site, built to awards-site standards — cinematic scroll, a custom WebGL ink shader, and a scroll-reactive grain layer, wrapped around a real booking flow.

Benchmarked against: Active Theory, Resn, Fantasy Interactive.

What this is

Not a template. Not a component library. A handcrafted, single continuous scroll built section by section, with a real performance budget behind every effect.

SectionConceptKey interactionS1 — HeroCinematic entryVideo crushes through contrast/saturation, then dissolves into a live WebGL ink shader as you scrollS2 — WorkEditorial identitySticky featured image, word-by-word text reveal, horizontal work stripS3 — BookingDesire + urgencyStudio flashlight background effect, full two-step booking modalS4 — ArchiveClose the worldAsymmetric image grid, scramble/glitch statement, live clock, footer

Sections 3 and 4 are code-split and only mount once you scroll near them — the WebGL hero loads eagerly, everything downstream of it doesn't cost you a byte until it's needed.

Stack

React 19 + Vite 7 Tailwind CSS 4 GSAP 3 + ScrollTrigger — every scroll-driven animation in the site Lenis — smooth scroll, synced into GSAP's ticker so ScrollTrigger reads Lenis' scroll position, not the native one React Three Fiber + Three.js — the hero's WebGL canvas Custom GLSL — hand-written vertex + fragment shaders for the ink effect (no shader library) Redux Toolkit — small UI slice for cross-section state

Architecture

src/ ├── App.jsx ← Lenis setup, preloader gate, section mount order ├── lib/ │ ├── gsap.js ← GSAP registration + useIsVisible intersection hook │ └── performance.js ← device-tier profiling (see below) ├── components/ │ ├── Preloader.jsx │ ├── NoiseLayer.jsx ← global canvas grain, adaptive FPS │ ├── DeferredSection.jsx ← lazy-mounts a section once it's near the viewport │ ├── LazyImage.jsx │ ├── Section1/ │ │ ├── Section1.jsx ← orchestrator │ │ ├── Video.jsx ← hero video, scroll-driven crush │ │ ├── WebGLScene.jsx ← R3F canvas, custom FBM ink shader │ │ ├── CurvedLoop.jsx │ │ └── Text.jsx │ ├── Section2/ │ │ ├── WorkSection.jsx ← orchestrator │ │ ├── Image.jsx │ │ ├── Text.jsx │ │ └── WorkStrip.jsx │ ├── Section3/ │ │ ├── Section3.jsx ← orchestrator (lazy-loaded) │ │ ├── StudioBackground.jsx │ │ ├── BookingHeadline.jsx │ │ ├── BookingCTA.jsx │ │ ├── BookingModal.jsx ← two-step booking flow, local state only │ │ └── ProcessList.jsx │ └── Section4/ │ ├── Section4.jsx ← orchestrator (lazy-loaded) │ ├── Background.jsx │ ├── Grid.jsx │ ├── ImageCard.jsx │ ├── GlitchStatement.jsx ← scramble/settle text effect │ ├── Counter.jsx │ ├── Clock.jsx │ └── Footer.jsx └── app/store.js ← Redux store

The ink shader

The hero's WebGL scene runs a hand-written GLSL shader — no noise package, no shader library:

6-octave fractal Brownian motion (FBM) built on a hash-based value noise function Mouse-driven ripple: distance-to-cursor feeds a damped sine wave into the vertex displacement A scroll-controlled dissolve uniform blends the ink surface in as the hero video fades out

Performance engineering

This is the part that doesn't show up in a screenshot. lib/performance.js profiles the device on load — touch capability, hover support, deviceMemory, hardwareConcurrency — and derives a full quality tier from it:

WebGL pixel ratio and shader mesh segment count both scale down on low-end/touch devices Grain and noise layers run at a capped, device-appropriate frame rate (as low as 10fps on low-end hardware) instead of every frame DeferredSection uses an intersection observer to keep Sections 3 and 4 out of the bundle's critical path entirely until they're within 1200–1400px of the viewport

Booking flow

A real two-step modal — name, date, time, style, idea, then a confirmation screen with a booking summary. No backend, all state local (useState); built to prove out the full interaction, not just the UI shell.

Code conventions

const arrow functions only, no function declarations export default at the bottom of every file Tailwind for layout/spacing/type/color; style={{}} reserved for gradients, willChange, mixBlendMode, and anything GSAP animates directly No clamp() — responsive breakpoints instead One JSX prop per line

Running it locally

bashnpm install npm run dev # http://localhost:5174 npm run build

Status

Actively in progress — mobile performance pass most recently in flight. Sections 1–4 are functionally complete; polish (cursor treatment, inter-section transitions) is the next layer being added on top of the current build.

About

A cinematic, scroll-driven tattoo studio website that turns ink art into a premium visual storytelling experience using video, motion, and editorial UI design.

Resources

Stars

16 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages