Site Fix Notes: 3D First Visit, Mode Isolation, and Global Edge Caching
This production-readiness pass makes capable desktop visits enter Studio directly, isolates Studio and Classic switching, reduces the delivered 3D asset, and restores static generation and global edge caching for localized archives.
The production review began with two visible problems. A first visit to the domain could land on the Classic homepage, and switching from Classic back to the 3D homepage could leave the page on its static scene texture for a long time before interaction became available.
They were not one loader bug. Homepage mode state, 3D delivery, locale transitions, Next.js dynamic rendering, and edge caching were all contributing. The fix therefore did not hide the wait behind another animation. It rebuilt the first-visit path from routing through the first valid Three.js frame.
Release Summary
- On desktops with suitable WebGL, device, and network capabilities, the homepage now enters through the Studio poster instead of rendering a usable Classic homepage and covering it later.
- A manual Studio or Classic choice remains stable during the current tab runtime and across locale changes, without leaking from one mode into the other.
- The production GLB fell from
9,620,604to6,524,276bytes, a32.18%reduction, while preserving scene hierarchy, cameras, materials, and interaction names. - Progress now represents received bytes, and
100%means that the first valid authored frame has taken over—not merely that the file download ended. /zh,/en, and top-level blog, photography, about, and other archive pages are statically generated again and served directly from Vercel's edge cache.- Cloudflare continues to provide DNS and the
img.huzejun.comR2 image origin. Website HTML and the Studio GLB are delivered directly by Vercel, keeping the two delivery responsibilities explicit.
Two Homepages Need Two Explicit States
Studio is not a visual effect layered over Classic. It has its own poster, Canvas, room navigation, and interaction state. Classic is the complete alternative selected by the visitor or used on mobile, with reduced motion, without WebGL, on constrained connections, or after runtime failure.
The previous mode choice did not have a boundary narrow enough to avoid persistence, yet stable enough to survive a locale remount. A visitor could select Studio from Classic, then change language or recreate part of the tree and trigger the default decision again.
Manual mode now lives only in the current page runtime on window:
- Changing language from Studio remains in Studio.
- Changing language from Classic remains in Classic.
- A hard reload does not persist the choice and writes no cookie,
localStorage,sessionStorage, or URL parameter.
This intentionally short lifetime prevents mode leakage during one visit without allowing a months-old preference to override today's device capabilities. Automatic fallback is more precise too: saveData, slow-2g, and 2g prefer Classic, while an ordinary 3g label no longer rejects Studio by itself. Mobile, coarse pointers, reduced motion, missing WebGL, initialization failure, and context loss still go directly to Classic.
The Poster Is the First Frame, Not a Waiting Room
A cold 3D startup cannot guarantee that a Canvas is ready when the HTML arrives. That does not require a black screen, a long start gate, or a complete Classic page used as temporary underlay.
Studio now begins with a scene poster exported from the same Blender camera. The room navigation, language control, and Classic switch are immediately available. Three.js prepares the scene in the background and replaces the poster in place only after the first valid frame. Because poster and Canvas share the composition, takeover does not introduce a camera jump.
Progress is based on bytes actually received. It moves through 1–99% while loading and reaches 100% only after the scene has drawn successfully. Download complete, parse complete, and interactive no longer collapse into one misleading event.
Breaking Cold Start into Optimizable Stages
The production scene remains a same-origin GLB with no runtime dependency on online models, textures, or iframes. The geometry optimizer removes only vertex attributes unused by their materials. It stripped 594 redundant attribute sets while preserving 223 meshes, 223 primitives, 240,894 triangles, 29 materials, 21 images and textures, 379 nodes, and 6 cameras.
The delivery gates now include:
- A
7 MiBtarget and a16 MiBhard file ceiling. - A
128 MiBestimated GPU allocation ceiling. - Build-time verification of the manifest, hashes, asset structure, interaction roots, and licence records.
The network path begins with a 64 KiB Range probe and validates both 206 and Content-Range. Standard clients fetch the four remaining segments in parallel; constrained clients use two. When an origin responds with a full 200, the same response body is reused rather than downloaded again. KTX2/Basis setup runs alongside the request, and loaders are disposed immediately on abort or locale remount so one scene cannot accumulate duplicate decoders and requests.
In one clean, uncached, constrained browser run, the old implementation remained on its poster state after roughly 70 seconds. The new path showed its poster and working controls at about 2.68 seconds, reported 26% real progress at about 5.75 seconds, and handed over to interactive WebGL at about 8.78 seconds. These timings are not a promise for every device. They show that the remaining wait now corresponds to real transfer and decode work instead of mode remounts, duplicate loaders, or an incorrect page underlay.
Restoring Static Home and Archive Pages
The 3D file was not the only bottleneck. After deployment, localized pages that should have been static were marked dynamic by a locale cookie and request-time APIs. Even a normal blog or photography archive visit could cross regions to a function instead of hitting the nearest cached page.
The locale layout now generates static parameters for zh and en, sets the request locale before rendering, and disables the unnecessary locale cookie. Photography archive filters remain client-controlled URL state, so the server page no longer waits for searchParams. This restores prerendering for /zh, /en, blog, photography, calligraphy, about, resume, and the other top-level pages.
Photography and calligraphy detail pages still use query-aware previous, next, and return links. Those routes were not blindly forced static, because a performance change should not break filtered navigation semantics.
In production checks, warmed /en and /en/photography responses carried both X-Nextjs-Prerender: 1 and X-Vercel-Cache: HIT. They set no NEXT_LOCALE cookie and contained no private, no-cache, or no-store directive. Across global probes, most warmed HTML TTFB samples fell around 67–304 ms. A Johannesburg retry was about 597 ms, a useful reminder that edge caching removes avoidable cross-region computation but cannot erase real network distance and node variance.
Production Verification
The release was not accepted merely because it opened locally. Content, code, 3D delivery, and the production build all passed their complete checks:
- ESLint, TypeScript, and content generation passed.
- All 377 automated tests passed.
- Studio manifest, GLB, interaction-root, and licence verification passed.
- The Next.js production build completed with 406 static outputs.
- Real-browser checks confirmed no Classic content flash on the first Studio frame, Studio-to-Studio locale switching, Classic-to-Classic locale switching, and exactly one Canvas in Studio.
- Vercel multi-region HTML, full static assets, and GLB Range delivery were rechecked after deployment.
The main conclusion is that a fast 3D homepage is not achieved by compressing one model alone. The first-paint state must be honest, mode boundaries stable, transfers and decoders cancellable, static pages genuinely cacheable at the edge, and fallbacks based on usable capability instead of a device label. Only when those parts agree does Studio become a production homepage rather than a 3D demo that happens to work on a development machine.

