All work WEB PLATFORM / 3D live

25  /  WEB PLATFORM / 3D

Esmee — Give a Hand

$8,800 funds one prosthetic hand, and the site had to move strangers to give before anyone had applied.

July – August 2026Next.js 15 / React 19TypeScriptTailwind CSS 4GSAP + LenisBlender 5.1 (EEVEE)Canvas frame scrubbing (shelved)React Three Fiber (shelved)Vercel

The challenge

Esmee Innovations Asia builds a prosthetic arm in Malaysia and crowdfunds one hand at a time. An amputee records a three-question video, Esmee verifies the medical documents privately, and every sponsored dollar is matched dollar for dollar, so the gift arrives doubled. What I had was an engineering STL of the arm and five mockups sent over WhatsApp, four HTML pages and a PDF. I had to make the site move a stranger to fund an $8,800 hand for a person who had not applied yet.

What I built

I built it twice. The first build ran on Next.js 16 and React Three Fiber, with a hero clip I exported to 241 WebP stills and scrubbed frame by frame against scroll; it runs locally and was never deployed. I started a second codebase two days later on a different stack and a lighter art direction: Next.js 15, GSAP and Lenis, built around a film I rendered in Blender from the client's own 3.88M-triangle CAD assembly. That one shipped, and it is what the link opens.

How it works

THE CLIENT'S FILE THE GUARANTEE An engineering assembly 3.88M triangles, 125 parts SET BY SCRIPT, PER PART Direction radial, out from the spine Distance a deterministic hash Delay normalised X × 45 frames Rendered in Blender 5.1 on EEVEE A loop with no seam every part back to its start Apart wrist to fingertip, and back together the same way the arm turns exactly once per cycle, so there is no visible restart
The mechanism, drawn from the build.

What it looks like

Esmee — Give a Hand — screenshot
Captured liveWEB PLATFORM / 3D

The detail

01

CAD, choreographed

The client supplied an engineering assembly, not a web model: 3.88M triangles in 125 loose parts. I wrote a script to separate, name and normalise them, then give each part a radial vector out from the arm spine, a deterministic per-part hash for how far it travels, and a delay of its normalised X position times 45 frames. The hand comes apart wrist to fingertip and reassembles in the same direction, and the choreography is parameters rather than hand-placed keys, rendered in Blender 5.1 on EEVEE.

02

241 frames, not a video element

Browsers seek, hold and drop frames when a video is scrubbed, so in the first build I exported the hero clip to 241 WebP stills and drew them to a canvas, one frame per scroll position. They load coarse to fine, every 16th then 8th, 4th, 2nd, all, twelve at a time; if the exact frame has not arrived the canvas draws the nearest one it has rather than stalling. I map scroll through an inverse smoothstep, because the clip is dark at both ends and the clasp in the middle is the part worth dwelling on.

03

Built, then shelved

Both builds lost their centrepiece. The first carried a complete React Three Fiber scene, about 240 lines driving a 1.4 MB Draco hand decimated from 3.89M triangles to 150k, with a seven-bone rig and a grip loop. I cut it for reading generic beside a filmed clip, and cut the second build's 560vh pinned scroll film too, replacing it with an assembly loop where every part returns to its exact starting position and the arm turns exactly once per cycle, so there is no visible restart.

04

Deleting the numbers the mockups came with

The supplied mockups carried platform stats: 47 hands sponsored, 312 waiting, $284K raised, nine countries. On a site with no applicants yet all four would have been untrue, so I replaced them with four facts true from day one, and stripped unverified device claims sitewide. Stock photography is atmosphere only and labelled as illustrative, the sponsor page states outright that the prosthesis pictured is not a myHand, and I wrote the rules into a credits file so they survive later edits.

05

The applicants have one hand

In-browser recording assumes a working camera and the dexterity to hold a device steady while operating it, so I made it one path rather than the path: uploading an existing file sits beside it as an equal option, every control is a single large tap target, and nothing needs a drag or a long-press. Nine screens autosave to a draft with the password stripped, focus moves to the new heading on each step, and I replaced the mockups' CSS radio-tab trick with real ARIA tablists so panels are announced correctly. My own audit later caught where that discipline stops: below 880px the dashboard shell stacks its sidebar above the content, so a tab tap changes nothing visible.

The outcome

The second build is live on Vercel and current. Every route returns 200, and the fabricated mockup stats appear nowhere in the served HTML. I have applied every item of the client's written change document except the palette change, which is deferred. What sits behind the film is frontend only, with no payment processor, database or authentication, a mocked donation modal and placeholder stories; the first build was never deployed at all. I put that earlier build through a frontend taste audit of my own and wrote up the result: 97 findings, four of them critical, my own count against my own work.