An image website for a Hong Kong financial company. The whole page is one fullscreen 3D scene: a slowly rotating gold nugget, studio light that follows the visitor's cursor, and two minimal black plaques with the brand and contacts. No backend, no database, no trackers. Real-time 3D instead of a looping video file, shipped as static files.
The client works in capital raising and deal structuring. Their website did not need pages of text or a blog. It needed one thing: a first impression of quiet wealth. Restraint, gold, air. The kind of page a counterparty opens once before a meeting.
One screen, no scrolling, no menu. A black plaque with the company name on top, contacts below, and a piece of gold in the middle of the screen. Everything else was removed on purpose.
Five colors in the whole product. One typeface in light weights. Uppercase with wide letter spacing. The tagline sits on a frosted glass plate over the 3D scene. Nothing blinks, nothing slides in.
The obvious route is a looped showreel video. Instead the page renders a real-time 3D scene: it reacts to the visitor's cursor, weighs a fraction of a video, never stutters on loop and needs no autoplay permissions.
Pure static files on Cloudflare Pages. No CMS to update, no server to patch, no hosting bill. The site is a folder of files served from a global CDN.
The entire product lives in one Scene.tsx of 345 lines. The centerpiece trick: a point light glides after the cursor, so highlights and shading sweep across the rotating gold as if the visitor carried a lamp around a sculpture.
A pointLight of intensity 40 chases the pointer with a lerp factor of 0.14 per frame. The easing makes the light feel carried, not glued. Highlights and shading sweep across the rotating gold as the light moves, which reads as depth instantly.
The stock asset rendered as dirty gray metal: broken ao, light and metalness maps, wrong color spaces. The code switches the bad maps off and forces the physics of gold, metalness 1.0 and base color #d4af37, every time the model loads. Swap the model and the fix still applies.
Instead of loading a multi-megabyte HDR environment file, the scene builds its own softbox: 4 Lightformer rectangles inside a drei Environment, at intensity 0.75. Gold gets its long white highlights and the network transfers zero extra bytes.
ACES Filmic tonemapping at exposure 0.85 keeps the highlights on metal from clipping. Device pixel ratio is clamped to 1-2 so phones stay smooth, and the model preloads before the first frame. Antialiasing is on, the canvas asks for the high-performance GPU.
The nugget comes from a 3D stock. Loaded as is, it looked like gray dirty rock: broken maps, wrong color spaces, arbitrary scale. The scene sanitizes the material at runtime, in code, so any future model swap gets the same treatment for free.
Cloudflare Pages enforces a hard limit of 25 MB per file. The stock nugget arrived at 56.5 MB and simply could not be deployed. glTF-Transform compressed it to 22.2 MB, a 61% cut, without touching what the eye sees.
Meshopt compression re-encodes vertex data into a compact GPU-friendly stream, quantization stores positions and normals in fewer bits. Both are visually lossless at this scale. One mesh, one material, all three PBR textures preserved.
The GLB is the page's heaviest download by far. Cutting it by 61% is the difference between a luxury first impression and a loading spinner. The platform limit forced the discipline; the visitor gets the benefit on every load.
The runtime footprint is deliberately tiny: react, react-dom, three, @react-three/fiber and @react-three/drei. Nothing else ships to the browser.
TypeScript runs in strict mode and the build starts with a full type check: tsc first, then Vite. The Vite config is the default one, so the build is trivially reproducible. Deploy is Cloudflare Pages: push, build, done. The site is served from a global CDN on the free tier.
Nothing to maintain. No CMS to update, no server to patch, no admin panel to get hacked, no hosting invoice. The site is a folder of static files that will open the same way years from now. For a company whose product is trust, a page that can not break is a feature, not a compromise.
A small project built by the same rules as a large one: strict TypeScript, an explicit asset pipeline and a deployed scene to iterate on.
Every figure in this case study is verified against the project repository: file sizes, line counts, commit history, scene parameters. Estimates are labeled as estimates.
The client got a distinctive image site that behaves like a piece of brand identity: quiet, heavy with gold, alive under the cursor. It loads from a CDN, costs nothing to run and has no moving parts to break.
Business analyst and systems builder. Most of my portfolio is ERP and back-office automation: analysis first, then code. This case is the other side of the same craft. When a business needs presence instead of process, I apply the same discipline: clear requirements, platform limits as constraints, honest numbers, clean delivery.