Progressive delivery

A 3D avatar should not be a blank box

A GLB is atomic: nothing renders until the last byte lands. On a slow connection that is several seconds of empty frame on every embed. A3S repacks the same asset into nested layers, ordered so that any byte-prefix of the file is a complete, skinned, spec-valid avatar. The first layer is typically 4 to 17 percent of the original. Everything after it refines the mesh already on screen, over ordinary Range requests that any CDN already serves.

How a prefix can be a whole avatar

The packer simplifies the mesh successively, finest to coarsest, so the vertices surviving at each level are a strict subset of the level below it. Rank every vertex by the coarsest level it reaches, sort the vertex buffer by that rank, and each level's vertices become a contiguous prefix. A refinement then only ever appends bytes, never rewrites them. Skinning rides along untouched, because a mesh simplifier only emits a new index buffer over the vertex array it was given.

Layer 0Coarse mesh, full skeleton, thumbnail textures. A valid GLB on its own.
Layer 1New vertices, finer indices, and the animation clips.
Layer 2More geometry, applied in place on the live scene.
Layer 3Full detail and full-resolution textures.

Because layer 0 is a real GLB and not a custom blob, you can cut it out with nothing but curl and open it in any glTF tool:

npx @axis/avatar-stream pack avatar.glb -o avatar.a3s
npx @axis/avatar-stream inspect avatar.a3s
npx @axis/avatar-stream extract avatar.a3s -o preview.glb   # a 50 KB avatar
npx @axis/avatar-stream verify avatar.a3s --deep            # replays every layer

In a browser, the reader takes one ranged request to first frame and refines in the background, mutating the geometry already on screen rather than rebuilding the scene:

import { A3SPlayer } from '@axis/avatar-stream/three';

const player = await A3SPlayer.load('/api/avatar-stream?src=/avatars/michelle.glb', {
  THREE, GLTFLoader,
  onLayer: (l) => console.log(`level ${l.level}: ${l.triangleCount} triangles`),
});
scene.add(player.scene);   // already skinned and posed
await player.refine();     // detail arrives without touching the camera or pose

The format is documented in specs/AVATAR_STREAM.md and the endpoint serving these streams is /api/avatar-stream, which will hand you the layer table as JSON.