MilWebWhatsApp
Back to projects

Immersive site · historic stadiums in 3D (WebGL)

As Copas

8 stadiums in 3D · Cinema modePersonal project · live
Screenshot do projeto As Copas

Problem

Sports content on the web all looks the same: identity-less text lists, nothing anyone wants to share.

Solution

A World Cup tribute with 8 iconic stadiums modeled in 3D (stands, sectors and crowd), a Cinema mode for recording videos, a sticker album with viral audios and its own editorial identity. It's 100% static and fast even on 3G.

How it was built

A 100% static Next.js site (App Router), no backend and no database: history, trivia, stadiums and stickers live in typed TypeScript modules under src/data. That compiles to pure SSG, including dedicated per-stadium pages (/estadios/[slug]) generated via generateStaticParams, each with its own metadata and canonical URL. It's what explains the instant-load feel even on 3G: zero round-trips to any API to render content.

The differentiator is a procedural stadium generator in React Three Fiber. Each of the 8 iconic stadiums is described by a typed StadiumParams object: a radial profile of [radius, height] points revolved with LatheGeometry, its own sx/sz proportions and a unique "landmark", like an art-deco tower for Estádio Centenário, a flat overhanging ring for Maracanã, a giant arch for Wembley, a golden crown for Lusail and steel blades for MetLife. The terraced stands aren't an imported asset: they're a hand-built BufferGeometry, row by row, with vertex colors simulating sector aisles and alternating steps. And the crowd is an InstancedMesh of thousands of spheres placed by a deterministic PRNG (mulberry32, seeded from a hash of the slug), so it never varies between server and client and never breaks hydration.

The 3D experience was written to survive the real world: a WebGL context probe before mounting the Canvas (falling back to text and real photos if it fails), frameloop="never" when the card leaves the viewport or the tab goes hidden (IntersectionObserver plus visibilitychange, saving mobile battery), a recording hint that detects touch and user-agent so it never tells an iPhone user to press "Win+Alt+R", and a Cinema mode with a gently breathing camera (a soft sine on Y) plus a discreet watermark baked into the corner of the shot. That last one exists because the whole point of the site is to be shared, and an unattributed clip brings nobody back.

The project started with a senior-level self-audit of UX, UI, SEO and accessibility, run against its own code and the HTML served in production (14 dimensions, WCAG contrast ratios calculated pairwise). Most of the resulting roadmap has since shipped: a post-final countdown state, a share button on every stadium and trivia card, a WebGL fallback, off-screen render pausing, individual per-stadium pages for long-tail SEO and the Cinema-mode watermark. It's the kind of "side project treated like a real product" discipline that usually only shows up in paid work.

100% static

No backend, no database: content lives in TypeScript modules, full SSG including a dedicated page per stadium.

Procedural stadiums

8 stadiums in 3D generated from parameters (LatheGeometry + hand-built BufferGeometry), not imported models.

Deterministic crowd

Thousands of instances placed by a seeded PRNG: identical output on server and client, zero hydration mismatch.

Battery-aware 3D

Rendering pauses off-viewport and on hidden tabs; a friendly fallback kicks in if WebGL fails.

More screens

As Copas homepageProcedural 3D stadiumHistory and trivia section

Stack

  • Next.js
  • TypeScript
  • Three.js
  • React Three Fiber
  • Tailwind
  • Framer Motion

Ready to turn your idea into a digital product?

Tell me what you need. I reply fast and send a free quote, no strings attached.

  • Service contract
  • Post-launch support
  • You own the code
  • Free quote