Posy 1.5.0

Avatar poses in 115 bytes a frame

Posy is an open protocol for streaming humanoid body, finger and face poses between many participants in real time, over a WebRTC data channel or a WebSocket.

  • header + bone_mask16
  • 13 bone rotations52
  • position + hips height8
  • fingers, both hands24
  • 13 expressions + gaze15
A typical upper-body frame to scale, one cell per byte: 115 bytes. The same pose over VMC takes about 3.3 KB, 25–35× more (spec §6).
1

Is it for you?

A fit

  • You build multi-user avatar applications, in the browser or native
  • Many participants on ordinary internet connections
  • Body, fingers and face in a small frame, for VRM 1.0, VRM 0.x and PMX avatars
  • A small group hosted from one member's home connection

Not a fit

  • Lossless motion-capture recording: use VMC or BVH
  • A peer-to-peer mesh without a routing peer
  • Delta encoding, field stripping, props and scene state: deferred to v2 (spec §11)
2

How it works

Every pose frame goes from the sender to the routing peer and from there to the receivers. The routing peer owns the session clock, room membership, authorization and rate control. It is a role: a participant's machine, a VPS or a hosted SFU can take it.

Sendercaptures, encodes Routing peerforwards or drops Receiversinterpolate, render
  1. The sender declares, then sends. It declares the data types its tracker delivers and its avatar can show, and sends only those. A lost frame is never resent: every frame stands alone.
  2. The routing peer forwards. It checks a frame's length and that it stays within what the sender was granted, without decoding the rotations. It drops whole frames to keep a rate tier and never changes a frame's bytes.
  3. The receiver drives the sender's avatar. Each receiver applies the frame to its copy of the sender's avatar, interpolates between frames and conceals short gaps. Nothing is retargeted onto another rig.
Posy avatar space (§3)
Right-handed, Y-up, the model faces +Z, metres. Every humanoid bone has identity rotation in the T-pose, and each quaternion is a bone's rotation relative to its parent. It coincides with the normalized rig of VRM 1.0 runtimes; VRM 0.x and PMX rigs are adapted by the avatar driver on each end (§3.2). The wire is the same for all three.
Beyond the humanoid body (§2.6)
Tails, animal ears, further limbs and an avatar's own expressions are declared extras. The sender names them once as its avatar file does. A frame then carries 4 bytes per declared bone and 1 byte per declared value, with no names on the wire.
3

Build a frame

The formulas of spec §5. Toggle bones and blocks and read the frame's size, mask and declaration.

Smallest-three: one quaternion in 4 bytes (§5.3)

Drop the largest component, make it non-negative, quantise the other three to 10 bits each, pack them into one u32.

normalised quaternion (x, y, z, w)
–

The marked component (?) is the largest. It is dropped and rebuilt on decode.

packed u32
–
bit layout: index (2) a (10) b (10) c (10)
–
round-trip error
–

Worst case ≈ 0.25°. The identity gives ≈ 0.14°: 0 / 0 / 0 lies on a rounding tie.

4

Bandwidth

A sender uploads one stream, and its size does not grow with the room. What grows is what the routing peer sends back out, so viewers are cheap and a large room needs a good uplink. The figures follow spec §6 for the typical upper-body frame.

scenario
face block
–routing peer egress

–

Room–
Viewer downstream–
Sender downstreameveryone else's pose, without their own–
Routing peer ingress–
One stream at FULL / NORMAL / MINIMAL– / – / – kbit/s
5

Tables of the spec

What you look up while implementing. The full and normative text is spec/Posy.md.

A 64-bit mask. Bit n set means the quaternion of bone n is present; quaternions follow in ascending bit order. The colours are the declared types a bit belongs to. A relay refuses a frame that sets a bit of a type the sender was not granted, or one of bits 55–63.

6

Implementing Posy

  1. Read the core. §3 (avatar space), §4 (bones) and §5 (packets) of spec/Posy.md are what every implementation has to agree on. §2 is the session layer.
  2. Use or read the reference codec. posy-protocol on npm (reference/js): TypeScript, no runtime dependencies.
  3. Pass the test vectors. testvectors/frames checks bytes: decode each .bin, compare with its .json, refuse the ones marked reject. testvectors/poses checks meaning: axes and signs of legs and fingers.
  4. See it move. The loopback demo sends an avatar through a lossy channel, in the browser and without a server.
npm install posy-protocol