All writing
3 min readENGINEERING

I started this site the way most people do: a clean template, a light theme, a hero that said the right things and felt like nothing. It worked. It was also completely forgettable — the kind of site you close before you finish scrolling.

So I threw it out and rebuilt it as something closer to a field notebook: warm paper, ink, a single teal-pine accent, and a handful of interactions I actually cared about. These are the notes on why, and how.

The constraint that shaped everything: no animation libraries

The fastest way to make a portfolio feel generic is to reach for a motion library and sprinkle whileInView on everything. I wanted the opposite — motion that felt authored, not applied. So I set a rule: every "fancy" thing on this site is hand-rolled with the platform.

  • Scroll-driven reveals use CSS scroll-timeline and IntersectionObserver, not a library.
  • The per-glyph heading animation is a small component that splits text into spans and staggers a CSS char-reveal.
  • framer-motion isn't in the bundle. Neither is three.js.

That constraint did more for the final feel than any single feature. When motion is expensive to add, you only add it where it earns its place.

The globe is a <canvas>, not a 3D engine

The travel page has a slowly rotating dotted Earth with little hearts on the cities I've lived in and arcs tracing the moves between them. The obvious way to build that is three.js and a globe library. That's also ~500KB of dependency for something that is, visually, a field of dots.

Instead it's a 2D <canvas>: a baked grid of land coordinates, projected to a sphere and rotated each frame, with great-circle (slerp) arcs between cities. Drag to spin it. It's a few hundred lines and it ships nothing extra.

A résumé you type at

The hero is an interactive terminal. Type get-experience, get-projects, get-skills — it answers from a single source of truth (lib/resume.ts), with tab-completion and command history. It's a small wink at what I do for a living, and it turned the most boring part of any portfolio (the CV) into the part people actually play with.

Shipping it: Vercel + Cloudflare, ~20 minutes

The stack underneath is deliberately boring so the surface can be interesting: Next.js 16 on the App Router, Tailwind v4 configured entirely in CSS, next-themes for a dark-by-default toggle. Content lives in a couple of typed data files, so updating the site is editing an array, not fighting a CMS.

Deploy was a GitHub import into Vercel's free tier, a custom domain registered at cost on Cloudflare, and one CNAME record with the proxy turned off so Vercel issues the certificate. That's it. No pipeline to babysit — every push to main ships.

What I'd tell past me

Templates get you a site. They don't get you a voice. The parts of this build that took the longest — the globe, the terminal, the restraint around motion — are the only parts anyone remembers. If you're building your own corner of the internet, spend your time on the one or two things that could only be yours, and keep everything else boring.

More field notes to come.