...
Back

Shipping a 3D Avatar to the Browser Without Shipping the Studio

A rigged character with real animation is a large asset arriving over a network you do not control. Most of the engineering is about the arrival, not the rendering.

Shipping a 3D Avatar to the Browser Without Shipping the Studio

Shipping a 3D Avatar to the Browser 🧍

Putting a rigged, animated character on a web page is deceptively easy to start and unusually easy to ruin. The renderer is the part with tutorials. The part that decides whether it works for real users is everything between your storage bucket and the first frame.


The asset is the product decision

A character model carries a skeleton, skinned meshes, blend shapes for expression, materials, and textures. Textures dominate — it is entirely normal for them to be most of the file, and entirely normal for a model exported straight from an authoring tool to arrive at a size no mobile visitor will ever wait for.

Before any renderer work, decide what the model must do:

  • A viewer that idles and looks alive needs a full expression set and good materials.
  • A background element needs neither, and shipping them is pure cost.
  • Something that lip-syncs and emotes needs blend shapes preserved and accurately named, which constrains what you are allowed to optimize away.

The order that works is: cut what the experience does not use, compress what remains, then optimize the render loop. Doing it in the other order means tuning draw calls on a model whose textures are the actual problem.

Concretely, the reliable wins are mesh compression, texture formats that stay compressed on the GPU rather than expanding in memory, resizing textures to what the display actually resolves, and removing bones and blend shapes nothing references. Each is mechanical. Together they routinely take a model from "unusable on mobile data" to "fine."


Loading is a user experience, not a spinner

Large assets over real networks means a visible gap, and the gap is where most implementations give up and show a spinner.

What works better:

Never block first paint on the model. The page should be readable and interactive while the character loads. If the character is the page, render the layout, the copy, and a lightweight poster image immediately, and swap in the live model when it is ready.

Load in order of perceptual importance. Geometry and a low-resolution texture first gets you a recognizable character quickly; the high-resolution pass can arrive after. A character that appears slightly soft and then sharpens reads as loading. A blank box that snaps into existence reads as broken.

Decide deliberately for visitors who should not get it at all. Constrained devices, data-saver signals, reduced-motion preferences. The poster image is not a failure state; for some visitors it is the correct experience, and treating it as a first-class outcome is what keeps the page fast for everyone.

Handle the failure. Assets time out. Decoders fail on old hardware. There must be a path where the model never arrives and the page is still fine, and it must be tested, because it is the path a real fraction of visitors take.


Where the animation time actually goes

Separating animation data from the character is the setup that pays off — one model, many motions, each one small. The practical difficulties are rarely mathematical:

  • Naming is a contract. Animations drive bones and blend shapes by name. An export that renames anything produces silence: no error, no motion, nothing to debug except a diff of two name lists.
  • Retargeting is where assumptions surface. Motion authored for one skeleton applied to another exposes every difference in proportion and rest pose, usually as limbs intersecting the body.
  • Idle motion is worth more than it costs. A character that breathes and shifts weight reads as present. A perfectly still one reads as a crashed render, even at sixty frames per second.

The constraint worth designing around

A 3D character is the only element on the page with an unbounded appetite: more polygons, larger textures, more simultaneous animations — each improvement is available and each one is paid for by the visitor, on their device, on their connection.

So set the budget first, as a number, before any of the fun parts. A total asset weight and a target device. Then every later decision — this texture size, this expression set, this extra motion clip — has a place to be checked against, and the model does not quietly grow through twenty individually reasonable commits into something only your workstation can load.