Cinematic dark-forge landing page that surfaces the 9 subdomain portals. The scroll wheel rotates the 3D iron gears on either side; spark particles emit at the meshing points when scroll is active.
- Next.js 14 (App Router) + TypeScript
- React Three Fiber + drei + postprocessing
- Lenis (smooth scroll)
- Tailwind CSS (UI overlay only — the canvas is pure R3F)
- Zustand (shared scroll-velocity store)
- Geist Sans / Orbitron / JetBrains Mono (via
next/font)
npm install
npm run dev
Open http://localhost:3000.
app/
layout.tsx — fonts, Lenis provider
page.tsx — full-page composition
globals.css — forge palette, hero-title metal gradient, portal-card styles
components/
Scene.tsx — main R3F <Canvas>: gears, lights, particles, post-fx
Gear.tsx — single gear, loads shared .glb + applies baked PBR textures
Sparks.tsx — GPU-instanced sparks at meshing points (scroll-driven)
Embers.tsx — slow ambient drifting embers (always on)
PostFX.tsx — Bloom / Vignette / Noise / ChromaticAberration
LenisProvider.tsx — sets up Lenis, writes scroll/velocity/activity to store
ui/ — NavBar, HeroText, PortalGrid, StatusBar
lib/
portals.ts — 9-subdomain portal data
scroll-store.ts — Zustand store
public/
models/ — gear_large.glb, gear_medium.glb, gear_small.glb
textures/ — gear_basecolor.png, gear_normal.png, gear_roughness.png, gear_metallic.png
- Scroll → rotation gain:
Gear.tsx, the8inMath.PI * 8 * speed. Lower = lazier, higher = spins more per scroll. - Scroll inertia:
Gear.tsx, the0.07lerp factor. Lower = heavier feel. - Spark emit rate:
Sparks.tsx, the80in1 + activity * 80. Higher = more sparks at peak scroll. - Bloom intensity:
PostFX.tsx<Bloom intensity={0.9}>. Crank to 1.4 for more "hot" look. - Forge palette:
tailwind.config.tsforge.*colors.
Defined in lib/portals.ts. Edit there to change titles, descriptions, or icons. Icons come from lucide-react.
The gear .glb files and PBR .png textures were baked from Blender (see the
separate phase1_gear_v2.py / phase2a_bake.py scripts). To regenerate:
- Install Blender 4.x
- Run
blender --background --python phase1_gear_v2.py - Run
blender --background --python phase2a_bake.py - Run
blender --background --python phase2b_export.py - Copy the new files into
public/models/andpublic/textures/
- Each gear glb is ~1.4MB (geometry only, no embedded textures)
- 4 PBR textures total ~5.5MB (the 2K normal map dominates at 3.8MB)
- For production, run the textures through a KTX2/Basis Universal encoder
(e.g.
gltfpack -tc) to shrink them ~5x with minimal quality loss - Six gears share one material and one set of textures — no duplication
- Target: 60fps on M1 MacBook Air. If you see hitches, drop the gear count or reduce Bloom mipmap levels.
The site is configured for static export (output: 'export' in next.config.mjs).
npm run build produces a fully static /out directory that any CDN can serve.
| Setting | Value |
|---|---|
| Framework preset | Next.js (Static HTML Export) (or None) |
| Build command | npm run build |
| Build output directory | out |
| Root directory | (leave empty) |
| Node version env var | NODE_VERSION = 20 |
- Push to GitHub (any name — e.g.
adarshrust-root). - Cloudflare dashboard → Workers & Pages → Create → Pages → Connect to Git → pick the repo.
- Build config: use the table above.
- Add environment variable:
NODE_VERSION=20(CF Pages defaults to Node 18 which can hit edge cases with Next 14). - Click Save and Deploy. First build takes ~3 min.
- Once deployed, you'll get a
*.pages.devpreview URL — verify the gears render and scroll works.
Since your domain is already on Cloudflare:
- Pages project → Custom domains → Set up a custom domain
- Add
adarshrust.com— CF auto-creates the CNAME record. - Add
www.adarshrust.com— same. - Wait ~30 seconds for cert provision. Done.
The 9 portal links (portfolio.adarshrust.com, etc.) won't resolve until you point
them somewhere. Each subdomain needs its own DNS record in the Cloudflare DNS panel.
For now, the easiest pattern:
- Create a placeholder Pages project for each subdomain that just shows "Coming soon — go back to adarshrust.com", OR
- Add a CNAME from each subdomain to your root Pages project — every link will land on the same site until you replace it.
Edit lib/portals.ts to add/remove portals or update URLs.
npm i -D wrangler
npx wrangler pages deploy out --project-name=adarshrust-root