OSC runtime (M5)
avatar-osc speaks VRChat's OSC parameter protocol, and avatar-osc-gestures
layers the analog-gesture daemon on top. This is the live layer that drives — and observes — an
already-uploaded avatar: avatar osc send|input|monitor|capture|replay|change|query|gestures.
Full text: docs/reference/osc-runtime.md.
The parameter protocol
avatar osc send VRCEmote 3 # /avatar/parameters/VRCEmote (int)
avatar osc input Vertical 0.5 # /input/Vertical, reset to 0 on exit
avatar osc monitor # print every update VRChat broadcasts| Address | Direction | What |
|---|---|---|
/avatar/parameters/<name> | both | Expression parameters: bool / int / float, typed by the avatar's OSC config. |
/input/<axis|button> | to VRChat | Locomotion axes and buttons; axes reset to 0 when the sender exits. |
/avatar/change | both | Avatar switch (blueprint id); VRChat also emits it on every change. |
avatar-osc handles /avatar/parameters/*, /input/* (axes/buttons,
reset-to-zero), and /avatar/change, split into a pure codec
(encode/decode rosc::OscMessage, fully unit-tested) and a thin non-blocking
ParamClient UDP transport (send to VRChat :9000, poll :9001). query parses an
avatar's OSCQuery config JSON (AvatarConfig: parameter names, OSC type tags, read/write
access) offline.
avatar_input::osc — that carries
/tracking/* transforms to drive a local rig, not avatar parameters. They never mix.
Parameter capture & the gesture cross-tab
avatar osc capture [--seconds N] [-o events.jsonl] records the parameter stream VRChat
broadcasts and reduces it to the report that answers "what does my controller actually
deliver?": per-parameter update counts and value ranges, plus the gesture cross-tab
— for every GestureLeft/GestureRight value held, how often it was entered
and the range its …Weight float covered. A missing row or a weight range stuck at
0.000..0.000 localizes a gesture bug to the input/binding side in one session; healthy rows
put the FX layer at fault. Raw events are appended as JSON lines as they arrive, so a cut-short session
keeps its data.
Both capture front-ends advertise themselves over OSCQuery
(avatar_osc::oscquery, --no-advertise to opt out): a hand-built mDNS responder
answers PTR _oscjson._tcp.local while a tiny HTTP responder serves the
?HOST_INFO handshake and a parameter tree exposing /avatar — which tells
modern VRChat to route its parameter output to the advertised UDP port, whatever it is, removing the
fixed-9001 assumption. Scope: same-host VRChat (the advertised address is 127.0.0.1).
The same logic ships as a standalone, dependency-slim binary avatar-gesture-capture that
cross-compiles to a double-clickable Windows .exe for the machine that actually runs VRChat.
Offline controller replay
avatar osc replay <events.jsonl> --controller FX.controller [--layer NAME] [--timeline]
runs a captured parameter log through the controller's state machines offline — no
Unity — and prints the state timeline each layer actually went through: visits, dwell times, and the
blend-parameter range covered inside each state. Capture proves what VRChat delivered; replay
proves what the controller did with it. Simulated semantics are the subset the generated
controllers use (ordered Any-State + state transitions, m_CanTransitionToSelf, the six
condition modes); crossfades are instantaneous and exit-time transitions are not modelled.
The analog-gesture daemon
avatar-osc-gestures is the "Vive advanced controls on any hardware" feature: read a
controller's analog trigger/grip, map to a VRChat gesture (GestureLeft/Right int 0–7)
plus an analog Gesture*Weight, and send via ParamClient. It is deliberately
glam-free — it defines its own minimal AnalogSource rather than
depending on avatar-input, so the cli graph stays out of glam.
- Pure
HandMapping— deadzone rescale(dz,1] → (0,1], Fist by default, optional grip gesture. GestureFrame::updates_sincechange detection — only changed params re-sent.GestureDaemonloop (tick/run/run_for) over aParamSink.
On-device input is OpenXR (an AnalogSource adapter, pending); headless uses a deterministic DemoSource triangle sweep.
avatar osc send VRCEmote 3
avatar osc query path/to/avatar-osc-config.json
avatar osc capture --seconds 60 -o events.jsonl
avatar osc replay events.jsonl --controller FX.controller --timeline
avatar osc gestures --seconds 10