/ recent work / threesided

threesided.com

The site you're on. Zero UI frameworks, hand-written CSS, view transitions, a lab of 18 live tech demos. The showcase is the demo.

Year
2026
Role
Solo build (with Claude as engineering pair)
Status
live
Stack
  • Astro 6
  • Vanilla CSS
  • Three.js
  • GLSL
  • Web Audio
  • Netlify

The pitch is “bleeding-edge web work, shipped without the bleeding.” The site has to back it up — every page is a working example of what we’d recommend a client build.

The problem

The previous threesided site was on Webflow. It looked fine. It said the right things. It also looked exactly like every other Webflow site, which is the opposite of the pitch.

A contractor who sells “we’ll build you something custom” can’t lead with a template. Visitors land, see a competent-but-generic marketing site, and the rest of the conversation has to fight the impression.

The brief was simple: rebuild it as a working demonstration of the work. Not a portfolio of the work — a portfolio that is the work.

The approach

We pulled the new site out of Webflow into Astro, and made two decisions that shaped everything after.

Zero UI frameworks. No Tailwind, no shadcn, no component library. Hand-written CSS using custom properties, the native cascade, and a small set of building blocks. The site is ~2 kB of JS on the homepage. Every “look at this nice thing” actually is the nice thing — built from the platform, not papered over with components.

A lab section, not a portfolio section. Instead of static project cards, we built /lab — eighteen live, working tech demos. WebGPU compute particles, a WebGL2 fluid solver, a CSS Houdini paint worklet, native scroll-driven animations, anchor-positioned popovers, an AudioWorklet synth — plus Three.js scenes, GLSL shaders, View Transitions, Rive, Spline, custom cursors. Each one is a real interactive page, not a screenshot. The point isn’t to show what we could build — it’s to let visitors poke at it.

What we built

Twenty-eight pages, all server-rendered, all under 1 MB. The skeleton:

  • /work — every shipped project, filterable by tech
  • /services — three engagement angles, each with a deep page
  • /lab — 18 live tech demos, plus a Three.js “Lattice” hero scene (synthwave grid + tetrahedra swarm + procedural mountain ridges + cinematic camera drift)
  • /journal — short technical notes in calibrated voice
  • /process — engagement model, sprint rhythm, ranges, what’s in, what’s not
  • /about / /career / /contact / /uses — the personal pages
  • /resume — noindex, off-nav, full named career for when someone asks

Plus per-page OG image generation (auto-picks up new routes), structured data on every page (Person, Organization, Service, BlogPosting, CreativeWork), a Netlify Forms contact endpoint with a /contact/thanks confirmation, and a /journal content collection with markdown frontmatter validation via Zod.

Trade-offs we made

No UI framework cost us nothing. Modern CSS — clip-path, transform-3d, custom properties, color-mix(), scroll-driven animations — covers 90% of “fancy hero” use cases at zero KB. The remaining 10% is Three.js, which we use deliberately where 3D depth needs to be real.

Self-hosting all fonts cost us a CDN dependency. Latin woff2 subsets are preloaded in <head> via Vite ?url imports. The site never makes a request to Google Fonts. CLS is zero across every audited page.

Building demos instead of writing about them cost us the easy listicle. “10 Three.js patterns I use” would have been easier than building 12 working examples. The bet was that working examples sell more than blog posts about them. Early data says yes.

Recent shipping

  • GA4 analytics, installed sitewide without denting the 100/100/100/100 Lighthouse scores — the tag loads async, high in the head.
  • Cookie consent that only appears where it’s required. Google Consent Mode v2 gates the EEA, UK, and Switzerland by IP; everyone else gets analytics on by default and never sees a banner.
  • A plain-language /privacy page, plus a “manage cookies” control that reopens the consent panel from anywhere on the site.

Results

After the lighthouse pass:

PagePerfA11yBest PracticesSEO
/100100100100
/lab100100100100
/lab/metaballs100100100100
/lab/audio-reactive100100100100
/lab/view-transitions100100100100

Page weight is ~50-150 KB on most pages (excluding the demo-specific Three.js dynamic imports). LCP is consistently under 200ms on Netlify’s CDN. CLS is zero everywhere.

The honest test isn’t Lighthouse — it’s the conversation that happens after a visitor lands. We’re three weeks in and the inbound is qualitatively different from the Webflow days. People who write in have already opened /lab/metaballs or /lab/view-transitions and want to talk about how it was built. That’s the funnel we wanted.

What’s ongoing

The site is iterated weekly, not annually. Recent additions: journal posts in calibrated voice (technical notes from real shipping), portfolio entries for Dungeon Jerks / Vegas Kiddos / Desk Gremlins / FuhNNY, project hero images synced from live repos, a Netlify-Forms contact endpoint, mini-preview animations on every lab card, six bleeding-edge lab demos (WebGPU compute particles, a WebGL2 fluid solver, a CSS Houdini paint worklet, native scroll-driven animations, anchor-positioned popovers, an AudioWorklet synth), a /process page that closes the “what does this cost” gap before someone has to email.

A GitHub Action runs Lighthouse against the production build on every PR — if a change drops any page below 95 on perf, it shows up in the PR comment. Defense in depth against perf regressions.

The site is a moving target. That’s the point.