Kai is roughly 30 primitives: rounded boxes, spheres, capsules. No sculpting, no retopo, no import pipeline. The topology view above is the actual geometry, and the silhouette survives at 48 pixels.
Built to move
I design, rig, and animate small personalities, then ship them straight to the browser. Everything moving on this page is procedural Three.js: no video, no downloaded models, just code running at 60 fps.
The cast
1 lead robot and 8 studies in material and timing: chrome, glass, matte clay, lacquered toy gloss, short flock, long wind blown fur. All procedural, all alive. Hover any card to see the bones.
Kai: a character study in code
Could a portfolio mascot do more than sit in a hero image? Kai is my answer: a character that is modeled, rigged, and animated entirely in JavaScript, light enough for a phone, and hopefully charming enough that you already hovered him.
The actual production asset, not screenshots. Every stage rebuilds live from the same source.
The brief
Most portfolio mascots are decoration: a PNG that never moves, or a Lottie loop that ignores you. I wanted a character with an actual production pipeline behind him: designed, modeled, rigged, and animated, then shipped as running code instead of a rendered video.
The constraint that shaped everything: he had to live on a real website without hurting it. That meant a tight polygon budget, no downloaded assets, and animation cheap enough to share a thread with the rest of the page.
3 decisions that shaped him
There is no skeleton. Each part hangs off nested pivot groups, and animation drives rotation and scale directly, like a stop motion armature. Cheaper than skinning, and it reads just as alive at this scale.
Every move is built from animation fundamentals: anticipation before the wave, follow through on the ear, squash and stretch in the hop, layered sines so the idle never visibly loops.
Process
Silhouette first: a rounded square head on a small body, readable at 48 pixels. The ear dot went through 9 placements before landing top left.
Grey primitives to lock proportion: head to body ratio near 1.5:1, feet tucked tight to the body so the float reads as one connected mass. The blockout stage in the viewer is this exact pass.
Rebuilt each primitive with clean subdivision so the wireframe reads as modeling loops, not triangle soup. The white on black grid you see on hover is the real mesh.
Nested pivots for head, arms, ear, eyes, and float. Control rings in the rig view mark every animatable transform: 14 in total.
Idle, blink, saccade, wave, and cursor follow, layered as independent sine and noise channels with offset periods so the combination never repeats exactly.
Merged materials, capped device pixel ratio, 1 shared renderer for every scene on this page. The live stats below are the receipt.
Motion breakdown · the timing sheet
1 sine on the root, shadow scaled in counter phase so the float reads as weight, not drift.
Layered sines with a pseudo random beat so blinks land off rhythm. Fully closed for 2 frames.
Eyes jump to a new offset and ease in over 120 ms, the same move your own eyes are making right now.
Base swing plus a 2nd harmonic at double frequency, fired in bouts with an anticipation dip before each one.
The ear trails every head turn by about a quarter second: follow through, straight from the 12 principles.
Every fur shell shears sideways, weighted by the square of its layer height: tips travel, roots hold, the coat looks alive.
What broke, and what fixed it
The topology overlay sat exactly on the surface and flickered. Fixed with polygon offset plus a 0.35 percent inflation: the overlay hovers a hair above the shell.
Blink, float, and wave on unrelated periods produced a visible hitch every 40 seconds. Retimed every channel to share a common period so the loop point is invisible.
1 WebGL context per scene tanked the frame rate. Rebuilt as a single renderer that scissor clips every scene from 1 fullscreen canvas: the reason this page holds 60 fps.
By the numbers
Measured live from this page. These stats are computed by the running scene, not typed in.
Where Kai goes next
v3 is a character sheet page of his own: expression states wired to site events, a walk cycle for page transitions, and a small emote set his chat widget can trigger mid conversation. Same rig, same budget.
Kai is also in the corner of this page right now. Click him and ask for a tour.