Clothing selection — each garment is its own skinned mesh sharing the
body's skeleton, so selecting is just toggling visibility. Colour is a material
value, which is how corps colours stay free.
One garment, every body type — switch body or build and the clothes
refit. The variants are glTF morph targets, and the SAME proxy binding that
fitted each garment generated a matching morph target on it. At runtime one
influence value is set on the body and on every garment. No per-body art, and
no per-body download.
Weight and frame are continuous — morph influences are floats, so the
same targets that drove buttons now drive sliders, and every value between them is
a valid body. The weight slider is signed: its sign picks which of two
targets to drive, so lean and heavy are one control.
Height is a skeleton edit, not a morph — and it has to be. Morph targets
are applied to the bind-pose vertices before skinning and never move the
bones, so a height morph would leave the knee geometry somewhere the knee bone
isn't: fine standing still, wrong the moment a clip bends the leg. Scaling each
bone's local translation moves the joints apart instead, so limbs lengthen without
thickening and the skull keeps its size. Clothing needs nothing — it is
skinned to the same bones, so it follows exactly.
Not everything should be a proxy — glasses are rigid. Binding them
to the body like clothing would shear the frame the moment a face preset widened
the skull, so instead they hang off the head bone and carry no morph targets at
all: one transform per morph target, six numbers each, solved in the exporter from
how far the nose, temples and ears actually moved, and summed at runtime from the
same influence values that drive the morphs. Ten styles for ~300 KB.
Emotes and reposing — glTF animation clips driving the shared
163-bone skeleton, crossfaded by three.js's AnimationMixer, holding
the final pose. Orbit is free camera control on top.
Why three.js
It reads glTF natively — skinning, morph targets and animation clips all came
across with no custom loader — and the same asset runs unchanged on the web and in
React Native through expo-gl / expo-three, or in
react-three-fiber if the app goes React. Babylon.js is the credible alternative and
has better built-in tooling, but it's heavier and DutDut's frontend is vanilla JS
with no build step, so three.js is the smaller commitment.
Honest limits
No subdivision here — this is the raw 13k-quad cage, so silhouettes are a
little faceted next to the Blender renders. Add a subdivided export, or normal
maps, when the art is final.
Body poke-through is possible with clothes hidden or on extreme morphs. The
Blender pipeline solves it with an under-cloth mask; the runtime equivalent is
an alpha mask texture on the body, per garment.
Body variants are a width profile, not real MakeHuman morph targets — those
ship with the app, not the repo. A real one flows through identically.