Frontend & WebGL

Next.js and Three.js: Building a Crawlable, Measurable WebGL Experience

Keep meaningful content available while progressively loading WebGL, with correct Next.js client boundaries and honest lab-versus-field performance measurement.

Next.js and Three.js: Building a Crawlable, Measurable WebGL Experience

A WebGL scene can add a distinctive visual identity while headings, navigation and actions remain ordinary, accessible page content. The challenge is coordinating their loading and exit states without making the page wait for the scene.

This article describes an implementation approach. It does not report a 100/100 Lighthouse result for this portfolio.

Keep the content independent of the canvas

Render meaningful headings, links and descriptions in HTML. Reserve dimensions for visual media so loading does not move surrounding content. An initially empty canvas does not inherently cause layout shift if its layout box remains stable.

Design loading, ready, failed and reduced-motion states. A failed model download should leave a useful page, not an indefinite progress animation. The fallback should occupy the intended composition and disappear when the healthy scene replaces it.

Put browser-only code behind the correct boundary

Next.js's lazy-loading guide documents next/dynamic and notes that ssr: false belongs in a Client Component, not a Server Component.

A practical structure is a server-rendered page containing semantic content and a small client wrapper responsible for importing and mounting the scene. The wrapper can manage readiness and a stable fallback. Keep browser APIs out of server execution.

Code splitting can reduce initial JavaScript, but a dynamic import alone does not guarantee that initialization waits until the browser is idle or that hydration cannot be delayed by later work.

This website uses React and Vite with prerendering; it is not a Next.js deployment.

Optimize assets without assuming free decoding

Measure geometry, textures and material complexity separately. The Three.js GLTFLoader documentation describes configuring decoders for compressed geometry and textures.

KTX2Loader transcodes supported Basis textures into a GPU-compatible compressed format. It still involves processing and requires capability detection; “no decode step” is an inaccurate description.

Compare visual quality and initialization cost on the intended devices. A smaller download can shift work into decoding, and excessive compression can damage a carefully authored material.

Measure lab and field behavior separately

Core Web Vitals guidance defines good thresholds as LCP at or below 2.5 seconds, INP at or below 200 milliseconds and CLS at or below 0.1, assessed at the 75th percentile.

A Lighthouse run is a lab observation under recorded conditions. Its Total Blocking Time is not measured field INP. Report device, browser, network, viewport, cache state and rendering mode with results.

Verify the complete scene lifecycle

Test healthy WebGL, unavailable WebGL, asset failure, reduced motion and navigation away and back. Check that offscreen rendering pauses where appropriate and that listeners, timelines and GPU resources are cleaned up.

Judge both the settled composition and the transitions. A page should remain readable and operable while the visual enhancement loads, runs and exits; a headline performance score cannot establish that on its own.

Sardar Mukhshaf

Sardar Mukhshaf

Full-Stack & DevOps Engineer

I build scalable web applications, secure backend systems, and production-ready cloud infrastructure. My engineering focus spans interface design, API architecture, deployment pipelines, and observability. Follow for more deep dives into system architecture, performance engineering, and emerging technologies.

More from the practice

Keep the useful ideas close.

Follow new articles with your preferred RSS reader.

Open the RSS feed ↗