MILWEB®
MILWEB®
MW/016LUMENSTUDIO

Lumen

Lumen — home screen
STUDIO
MILWEB
TYPE
WEBGL EXPERIENCE
STATUS
LIVE

02 / THE IDEA

Cinematic 3D site for architecture · React Three Fiber

Architecture studios need to convey sophistication and spatial mastery, and a plain website doesn't capture the feel of their work.

03 / EXPERIENCE
  1. 01 / 01

    No GLB: volumes, glass, furniture and pool are Three.js geometry generated in code, with a documented fallback path to swap in a real model.

    Lumen — 01
  2. 02 / 02

    6 positions interpolated with smoothstep, driven by GSAP ScrollTrigger + Lenis outside React's render cycle.

    Lumen — 02
  3. 03 / 03

    PerformanceMonitor samples the GPU over ~1.2s and auto-tunes canvas resolution between 0.6 and 1.6.

    Lumen — 03
  4. 04 / 04

    Cheaper glass, shadows and Bloom on mobile; parallax and micro-loops turn off with prefers-reduced-motion.

    Lumen — 04
Lumen's 3D hero with the procedural residence and animated headline

04 / UNDER THE HOOD

  • 01The house anchoring the hero isn't an imported model. It's procedural geometry, assembled in code from Three.js primitives (boxGeometry, pla…+

    The house anchoring the hero isn't an imported model. It's procedural geometry, assembled in code from Three.js primitives (boxGeometry, planeGeometry, icosahedronGeometry): cantilevered slab, glass facade, furniture, pool and landscaping, all placed with explicit coordinates inside a single House component. The reasoning is practical: with no GLB to download, decode (Draco/Meshopt) or host, the hero starts rendering on the Canvas's first frame. The Residence component already leaves the door open to swap in a real model. A RESIDENCE_GLB constant plus a SceneBoundary (a class-based error boundary catching WebGL and R3F failures) guarantee that if the GLB is missing or fails to load, the scene falls back to the procedural house without breaking the page.

  • 02The camera doesn't ride an external timeline library. It's a hand-written keyframe system in CameraRig, with six position and lookAt pairs (…+

    The camera doesn't ride an external timeline library. It's a hand-written keyframe system in CameraRig, with six position and lookAt pairs (facade, entrance, living room, staircase, balcony, pool) interpolated with smoothstep against a journey.progress value between 0 and 1. That progress is written by a GSAP ScrollTrigger pinned to the hero section, with Lenis driving the smooth-scroll rAF and emitting events that ScrollTrigger consumes (lenis.on('scroll', ScrollTrigger.update)). The bridge between the DOM and the R3F render loop is a mutable object (journey.progress) living outside React, read inside useFrame. That avoids re-rendering the component on every scroll tick, which at 60fps would mean hundreds of renders per second.

  • 03In-browser WebGL performance was treated as a budget, not luck. Canvas DPR is adaptive: a drei PerformanceMonitor measures real fps over ~1.…+

    In-browser WebGL performance was treated as a budget, not luck. Canvas DPR is adaptive: a drei PerformanceMonitor measures real fps over ~1.2s and a hook (useAdaptiveDpr) recalculates the internal resolution within a min/max range. A weak GPU drops to 0.6, a GPU with headroom climbs to 1.6, instead of getting stuck at 17fps with a fixed DPR. Mobile gets a deliberately cheaper scene: a simple transparent-reflection glass material instead of MeshTransmissionMaterial with real transmission, soft PCSS shadows and Bloom turned off, ContactShadows resolution halved. The 3D canvas also only mounts after first paint, via requestIdleCallback with a 1200ms timeout as a safety net, to avoid hurting LCP and TBT. And R3F's frameloop toggles between always and never based on an IntersectionObserver (useInView), so the scene stops rendering the moment it leaves the viewport.

  • 04Motion accessibility was built into the 3D scene itself, not just the DOM animations. usePrefersReducedMotion turns off the pointer-followin…+

    Motion accessibility was built into the 3D scene itself, not just the DOM animations. usePrefersReducedMotion turns off the pointer-following camera parallax and the ambient micro-loops (pool water breathing, warm interior light pulse) in both CameraRig and House, keeping the rest-state values already defined in JSX. Lenis's smooth scroll also gets out of the way entirely when prefers-reduced-motion: reduce is active, and the page falls back to native browser scrolling. That care, combined with a static poster (same framing as the fallback) shown behind the Canvas before it mounts, is what kept the hero fast and accessible without giving up the cinematic effect as the product's differentiator.

BUILT WITHReact · Vite · TypeScript · Three.js · R3F · SCSSCODE

06 / RESULT

Cinematic 3D experience

PERSONAL PROJECT · DEMO

[ I WANT SOMETHING LIKE THIS ]

07 / SELECTED SCREENS
Lumen's 3D hero with the procedural residence and animated headline
SCREEN 01Lumen's 3D hero with the procedural residence and animated headline
Architecture studio's services section
SCREEN 02Architecture studio's services section
Interactive 3D model section (drag to rotate, scroll to zoom)
SCREEN 03Interactive 3D model section (drag to rotate, scroll to zoom)
NEXT EXPERIENCE08 / ALVA ODONTOLOGIA

ALVA Odontologia

ALVA Odontologia

ALL WORK

ALL WORK