Building this portfolio: a black hole, a multi-pass renderer, and a hiring page
How the homepage works under the hood — a hand-written GLSL black hole, multi-pass WebGL composition, scroll-linked motion, and the deliberate choices that make it doubles as a recruiter-readable hiring site.
Next.js · Three.js · GLSL · Framer Motion · Performance
This note describes the 2025 version of this site. I have since rebuilt it without the black hole, and kept the write-up because the rendering work was fun.
The brief I gave myself
Most developer portfolios fall into one of two camps. Either they're a static template that says "I exist", or they're a 3D demo so heavy on visuals that you can't actually find the projects. I wanted neither.
The brief was simple: prove I can build something visually demanding, and at the same time make the hiring information so clean that a recruiter who hates 3D websites still leaves with the resume.
The stack
Nothing exotic.
- Next.js 14 (App Router) for the framework and the static-export-friendly routing.
- Three.js (raw, no React Three Fiber) for the hero scene, because the rendering is multi-pass and I wanted full control of the render targets.
- GLSL hand-written shaders for the noise prebake, the accretion disk, the gravitational-lensing distortion, and the post-processing composite.
- Framer Motion for scroll-linked section reveals.
- Lenis for smooth scroll on desktop, disabled on mobile and reduced-motion contexts.
- TailwindCSS for the design system.
- MDX for posts like this one.
The hero scene, explained
The black hole isn't a model. It's a few primitives plus shaders.
- A noise texture is pre-baked once into a render target using classic 3D Perlin noise. The accretion disk samples this noise at four offsets to fake animated turbulence without recomputing it per frame.
- The accretion disk is an inverted cylinder geometry with a custom fragment shader that maps a vertical gradient texture against the noise-distorted UVs.
- A starfield is 8,000 GPU points (3,000 on mobile) with per-vertex sizes and HSL colors, drawn through a tiny shader that fakes a soft glow with a
1/distancefalloff. - A lensing pass renders a simple "hole" billboard plus a horizontal disc into a separate distortion render target. That texture's red channel is sampled in post-processing to push pixels toward a convergence point — the projected origin of the black hole on screen — which is what gives the look of light bending around the singularity.
- The post-processing composite then samples the main scene, applies chromatic aberration scaled by the lensing distance, adds film grain, and vignettes the edges.
// The core distortion line, simplified:
vec2 dir = uConvergence - vUv;
vec2 uvD = vUv + dir * dist * 0.3;
// then chromatic-aberration sampling:
float r = texture2D(uScene, uvD + vec2(cos( 0.0), sin( 0.0)) * caStr).r;
float g = texture2D(uScene, uvD + vec2(cos( 2.1), sin( 2.1)) * caStr).g;
float b = texture2D(uScene, uvD + vec2(cos(-2.1), sin(-2.1)) * caStr).b;
Two render passes per frame, one composite to the screen. Earlier versions did three passes (an extra direct-to-screen render) and burned GPU for nothing — that was cut as part of a perf pass.
Performance tradeoffs I actually made
A 3D hero is an indulgence. It's worth nothing if it kills the page on a mid-range Android. So:
- Star count is halved on mobile (3,000 vs 8,000), antialias is off on mobile, and the device pixel ratio is clamped to 1.
- The animation loop pauses on
visibilitychangeso background tabs cost zero GPU. prefers-reduced-motiondisables camera roll and drift. The scene still renders so the page doesn't look broken, but the auto-rotation is gone.- All sections below the fold are wrapped in a custom
LazySectionthat usesIntersectionObserverwith a 300px root margin. Heavy components only mount when they're about to be visible. next/dynamicwithssr: falsefor the WebGL component itself, so the initial HTML is small and Lighthouse metrics aren't pulled down by the canvas mount.
The hiring layer on top of the visual layer
The 3D scene is the first impression. Everything below it is the actual hiring page.
- The About section is first-person prose with a sticky portrait on desktop. The bio walks from PC-building in 2017 to the work I'm proudest of today, with the resume numbers (169K records, 1.3M edges, +18 percentage points) embedded inside the story rather than as a list.
- The Recruiter Snapshot is a 30-second skim card: where I fit, how I work, proof points.
- The Key Achievements strip has six accent-colored cards covering hackathon standings, project metrics, and community reach.
- The Credentials section features Credly badges with brand-tinted glows and a Verified pill on each card. The whole card is the click target to Credly — no exposed URLs.
- Each project case study is its own page with its own SoftwareApplication and BreadcrumbList JSON-LD schema, plus a "View on GitHub" link to the source.
SEO that actually targets the name
The bigger goal is that searching "Ruchin Audichya" surfaces this site, not LinkedIn alone.
- A visually-hidden
SeoIdentitycomponent at the top of the page acts as the canonical text identity for crawlers — name, role, location, paragraphs of name-dense copy, and inbound links to every public profile. - The head emits five different JSON-LD blocks:
PersonwithalternateName,hasOccupationwith skills, awards, and credential URLs from Credly;WebSitewith aSearchAction;ProfilePage; a six-questionFAQPagefor direct-name queries; plus per-pageBreadcrumbList,Article, andSoftwareApplication. rel="me"links connect this domain to GitHub, LinkedIn, X, and Instagram so Google can confirm the same person owns those profiles.robots.tsexplicitly allows Googlebot, Bingbot, GPTBot, PerplexityBot, ClaudeBot, and Google-Extended. Being indexed by AI answer engines is a 2026 name-search signal too.geo.region,geo.placename, and ICBM coordinates point at Jaipur for local-pack signals.
What I'd change next
The honest list: better project screenshots in case studies (the current placeholders are weak), a real architecture diagram for MediFast AI's case study, the chancellor recommendation letters as an Endorsements section, a refreshed OG image that matches the current positioning, and a verified email domain in Resend so the contact form stops being routed through onboarding@resend.dev.
Most of those need an asset, not a code change. They're on the list.