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.
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.