PhysBone tuning
The first thing anyone does after wearing a migrated (or freshly authored) avatar is retune its
PhysBones: the hair is too floppy, the skirt flaps, the tips of a long chain go wild.
avatar physbone does that read-modify-write on the prefab file itself — a change is
a one-line command that lands on the exact component and leaves every other byte (fileIDs, references,
formatting) alone. It lives in avatar-migrate as the post-migration step but works on any
SDK3 prefab. Full text: docs/reference/physbone.md.
The verbs
avatar physbone list Avatar.prefab [--json]
avatar physbone set Avatar.prefab <TARGET> [tuning…] [--ignore N]… [--collider N]… -o Avatar.prefab --force
avatar physbone split Avatar.prefab <TARGET> --chain Hair_1 --chain Hair_2 [tuning…] -o … --force
avatar physbone stretch Avatar.prefab <TARGET> --factor 1.5 | --by 0.077 [--from-depth 2] -o … --force
avatar physbone flare Avatar.prefab <TARGET> --angle 10 | --scale 0.5 [--hinge-depth 1] -o … --force
avatar physbone nudge Avatar.prefab <TARGET> --out 0.008 [--up M] [--chain N]… -o … --force
TARGET is the PhysBone's root transform (a unique bone name or an A/B/C path),
the GameObject carrying it, or its &fileID — list prints all three,
and an ambiguous name errors with the candidates. Edits follow the shared write policy:
-o FILE writes (the input path with --force edits in place), no -o
prints to stdout, --dry-run reports without writing, --json prints a
before/after report (avatar schema physbone).
list — what a PhysBone is doing
For each VRCPhysBone (recognised by its DLL class reference): the object it sits on, the
root it simulates from, the chains it drives — bones per chain and their length
in avatar space — the total transforms simulated (the number VRChat's perf rank counts),
colliders, and the full tuning (pull/spring/stiffness, gravity + falloff, immobile, radius, limits,
grab/pose/collision flags), each with its curve if one is set. Each chain also reports
its flare: the angle between its first-bone→leaf direction and straight down.
multiChildType Ignore (the
default) the root stays put and each child starts a chain; with a single child the root is the
first bone. That is why splitting all-but-one chain off a root turns the root into a simulated bone.
set — retune one component, with per-chain curves
Every tuning value is optional; unset = keep. Values cover pull, spring, stiffness, gravity (+
falloff), immobile (+ type), radius, limit type + max angles, integration, version, multi-child,
collision/grab/pose flags, stretch/squish, isAnimated; lists take
--ignore/--unignore and --collider/--uncollider.
Curves (--pull-curve, --spring-curve,
--stiffness-curve, --gravity-curve, --immobile-curve,
--radius-curve, --max-angle-curve) take T:V,T:V,… keys
— T the position along the chain (0 = first bone, 1 = tip), V a 0..1
multiplier of the base value, which is how the SDK applies them. Keys are written with
tangents equal to the secants to their neighbours, so the Hermite curve Unity evaluates is exactly
piecewise-linear between your keys — no editor smoothing surprises. "More weight
at the ends" is a pull curve rising toward the tip plus a spring curve falling toward it.
The component body is re-rendered from a typed PhysBoneSpec read back from the prefab
(the same emitter the migration uses; the round trip is byte-stable and
test-pinned), so a set rewrites exactly one document and touches nothing else.
split — tune chains apart
Long pigtails and short bangs usually share one component rooted on Head/Hair,
and one set of numbers can't suit both. split moves each named chain onto its
own VRCPhysBone on that chain's first bone, tuned like the parent plus
whatever tuning flags you pass, inheriting the parent's colliders — and adds the chain to the
parent's ignoreTransforms. New fileIDs are derived from the bone path (stable across
runs). Component count goes up by one per chain (a perf metric: PC 4/8/16/32); simulated transforms
don't change.
stretch — a longer skirt or tail without a mesh edit
--factor F multiplies the local position of every chain transform at depth ≥
--from-depth below the root (default 2, which keeps a many-chain skirt's hinge ring in
place). --by METERS instead adds the same length to every chain — each chain gets
its own factor — which is what you want when chains have unequal bone counts: a uniform factor
grows a 5-bone chain more than its 4-bone neighbours and the hem grows peaks there; equal added length
keeps an even hem. --chain NAME=METERS overrides one chain — the knob for levelling
a hem whose sectors respond unequally. The skinned mesh follows its bones, so what hangs off the chain
gets longer. Only translations change — no rotation, no non-uniform scale, so no shear, and
PhysBone sees ordinary (longer) bones with the same radius.
flare and nudge — re-aim and re-seat the chains
A skirt whose chains tilt outward gets a funnel silhouette, and stretching along that tilt makes the
funnel wider. flare re-angles each chain in avatar space: the hinge transform
(--hinge-depth below the root) is rotated so the hinge→leaf direction makes the
target angle with straight down — --angle 10 for every chain, or
--scale 0.5 to halve each chain's current angle. One local rotation changes per chain, so
it swings rigidly like a panel and the skinned mesh follows. It is a rest-pose edit:
PhysBone takes the new pose as rest, and colliders still push the chain out where the new rest sits
inside them (thighs) — exactly the "drape over the legs" you want.
nudge shifts the hinge ring itself: --out METERS moves each chain's hinge
radially away from the root's vertical axis (negative = inward), --up METERS along +Y,
--chain NAME to restrict it. The panel shifts rigidly — drape, angle and hem shape
untouched — the fix when flare has swung a skirt's top ring inside a waistband.
Preview before Unity
avatar render --avatar model.fbx --pose Avatar.prefab -o out.png draws the FBX with every
bone's local transform taken from the prefab (matched by name, Unity's mirrored import undone), so what
you see is what Unity will show for that prefab — a stretched, re-angled skirt, anything.
--stretch 'Skirt_0_*:1.5' is the lighter one-knob form for previewing a stretch alone
(render).
Typical workflow — retuning a migrated skirt
The shape of the real (mikunpc) pass: split the pigtails onto their own components with
weight and damped tips; set calm values on what stays on Hair; firm up and
stretch the skirt; wear it, then iterate — switch a uniform --factor
to equal-added-length --by when the hem grows peaks, flare the funnel in to
hug the legs, nudge the top ring out of a waistband, and finally fix the runtime
silhouette (thin fabric radius, smaller leg-capsule colliders, real gravity/immobile) when the editor
pose and the in-game pose disagree.
list prints both radii; check them against the limb before blaming the pose. After a pass,
avatar lint should stay clean and
avatar stats reports the component-count change.