A Unity Editor tool that converts a VRChat SDK3 avatar into a ChilloutVR CCK 4 avatar — animator, menus, physics, contacts and face tracking — and hands you a clean starting point to finish by hand.
- VRCFury & Modular Avatar avatars work. It runs Fury's own builder (or NDMF's manual bake) first, then converts the baked result, so toggles, linked clothing and merged armatures survive.
- PhysBones become real physics — MagicaCloth2 or DynamicBone, no external tool.
- Readable output — clothing toggles come out as one
Toggle <name>layer each, driven by realboolparameters. - Bloat removed — GoGo Loco and SPS/OGB/PCS are stripped (one avatar went from 3088 to 240 of 3200 sync bits).
- Face tracking, your way — native
CVRFaceTracking, or the bundled CVR-VRCFT rig with eye tracking wired up. ARKit and Unified Expressions meshes both work. - ChilloutVR's native contacts — VRChat contacts convert one to one, with real proximity and no sync cost, using a system the CCK doesn't expose. Confirmed working in game. See below.
- Shaders that lose an eye get fixed — CVR renders single-pass instanced where VRChat renders double-wide, so shaders that never opted in draw into one eye only. AvatarBridge reports them and can patch a working copy. See below.
- Diagnostics that know ChilloutVR — the report names components CVR will silently delete on load, tracks its 3200-bit sync budget, and flags shaders its uploader will reject, rather than leaving you to find out in game.
- Avatar scaler — a
Height (M)menu control defaulting to the avatar's measured eye height, so it's the same size before and after and the number reads in real metres. - VRChat tracking control converts —
VRCAnimatorTrackingControlbecomes CVR'sBodyControl, so animations that take a limb away from IK still do.
(No VRChat SDK installed? The tool still runs in Setup mode and prepares any humanoid for ChilloutVR.)
The banner runs VRChat's blue into ChilloutVR's orange, and the step markers sit along it — because that's the trip your avatar is making.
AvatarBridge does the tedious ~90%. It does not make avatar setup brainless, and can't —
VRChat and CVR are different platforms and VRCFury setups vary endlessly. It assumes you know
your way around Unity: the Animator window, blend trees, the CVRAvatar component.
Every run writes a ConversionReport.md you're expected to read — act on each Warning,
Approximated and Skipped entry — and every conversion should be tested in ChilloutVR
before you call it done. The editor can't show you gestures, contacts or synced parameters
actually running.
| What | Version | Notes |
|---|---|---|
| Unity | 2022.3.22f1 | the version VRChat and CCK 4 both use |
| ChilloutVR CCK | 4.0.x | always required — it's what the tool builds for |
| VRChat Avatars SDK | SDK3 | required to convert; without it you get Setup mode |
| VRCFury / Modular Avatar | current | only if your avatars use them |
| MagicaCloth2 | optional | recommended physics target |
| DynamicBone | optional | alternative target; the free VRLabs stub is enough to convert |
Neither physics package is required — choose Convert PhysBones to → None and everything else still converts.
⚠️ Import order matters. Let Unity finish compiling after each step. Importing out of order can corrupt VRCFury data or leave broken scripting defines.
- Unity 2022.3.22f1.
- A copy of your avatar project — duplicate it. Never convert in your real upload project.
- ChilloutVR CCK 4.
- A physics package (optional).
- VRCFury / Modular Avatar — whichever your avatars use.
- Your avatars — import any that aren't already there, after VRCFury.
- AvatarBridge, last. Grab the
.unitypackagefrom Releases. It must live underAssets, notPackages— that's how the optional MagicaCloth2 / DynamicBone integration resolves.
One extra recompile after importing AvatarBridge is normal — that's it registering its scripting defines.
Tools → Avatar Bridge → VRChat to ChilloutVR Converter, then:
- Pick the avatar in your scene.
- Check the options — physics target, face tracking mode, height scaler. Defaults are fine for most avatars.
- Convert. Output lands in
Assets/AvatarBridge/Output/<avatar>/. Read the report, then test in game.
| VRChat | ChilloutVR | Notes |
|---|---|---|
| Avatar descriptor | CVRAvatar |
viewpoint, voice, visemes, blink |
| Expression parameters + menus | Advanced Avatar Settings | named after the menu control's label |
| Clothing / prop toggles | one Toggle <name> layer each |
pulled out of VRCFury's merged blend trees |
| Parameter types | real bool / int / float |
see below |
| Gestures | GestureLeftIdx / RightIdx ints |
analog fist curl stays native |
| PhysBones + colliders | MagicaCloth2 or DynamicBone | see below |
| Contacts | ChilloutVR's native contacts, or CVRPointer / trigger |
see below |
| VRC Constraints | Unity constraints | |
| FinalIK components | kept as-is | |
| VRC tracking / locomotion control | BodyControl |
hands a limb from IK over to animation |
| Jaw-flap lip sync | visemeMode = JawBone / SingleBlendshape |
rig-driven, no wiring needed |
| VRC Head Chop | FPRExclusion |
|
| Avatar cameras / listeners | removed | a stray Camera crashes CVR's asset filter |
PhysBone _IsGrabbed / _Angle |
GrabbyBones mod | optional mod, not bundled |
| Face-tracking blendshapes | native CVRFaceTracking or bundled rig |
see below |
| Menu Button controls | ordinary toggles | |
| Shaders without stereo support | patched copy in RehomedAssets |
optional, off by default — see below |
| VRCFury temp materials/shaders | rescued into RehomedAssets |
Fury deletes its temp folder on its next build |
GoGo Loco and SPS/OGB/TPS/PCS are stripped by default (both toggleable). CVR has its own locomotion, and the haptics stacks don't function there while eating most of the sync budget.
Structure transfers exactly; feel does not. Which bone the chain hangs from, which colliders it collides with, which transforms to leave out, whether it started enabled — all verbatim. The physics values start from a MagicaCloth2 preset instead of being derived from the PhysBone's.
That's deliberate. Earlier versions derived MagicaCloth2 settings from PhysBone settings — gravity scaled into m/s², spring inverted into damping, immobile into inertia. Every one of those looked reasonable and every one had to be walked back after a real avatar misbehaved.
The reason isn't that the numbers were wrong. The two systems are different kinds of simulation. PhysBones — like DynamicBone, which they replaced — are per-bone rotational springs. MagicaCloth2 is a particle position solver: it moves particles through space and reads bone rotations back out of where they land. A value meaning "springiness" to one doesn't mean anything in particular to the other.
So a stock MagicaCloth2 BoneCloth — a configuration tuned by the solver's own author — is where every chain starts. Three PhysBone facts do carry over, because they ask the same question on both sides rather than needing conversion: a chain with no gravity keeps none (presets ship their own, and Long Hair's 5.0 would make it fall for the first time in ChilloutVR), negative gravity points up, and immobile becomes world influence, which is the same 0–1 question measured the other way round. Pull, spring and stiffness are left alone — they have no counterpart. Each adjustment is named in the report, as are the PhysBone's own numbers, so you can tune from there:
tail — BoneCloth on the MagicaCloth2 "Tail" preset, 3 collider(s). Source PhysBone was pull0.2, spring 0.4, gravity 0, immobile 0.75, radius 0.02 — none of those transfer.
Stretch & squish, multi-child blending, Is Animated and angle limits are reported the same way,
each naming the field to change if that chain wants it.
Using DynamicBone instead? None of this applies — PhysBones and DynamicBone are the same kind of simulation, so that path maps values across 1:1.
None of these add arithmetic; they swap one author-tuned baseline for another, or copy a value verbatim.
| setting | default | what it does |
|---|---|---|
| Match a preset to each chain | on | Hair, tail, skirt, cape or accessory by bone name; otherwise a soft/middle/hard spring by how firmly the PhysBone held its rest pose |
| Fit the preset to the PhysBone | on | The three facts above — no gravity, upward gravity, immobile → world influence. Turn it off to get the preset exactly as its author wrote it |
| Cap particle radius to bone spacing | on | A safety rail: MagicaCloth2's radius is the particle size, and particles wider than the gap between bones shove each other apart |
| Transfer angle limits | off | Copies each limit angle across. |
| Auto-assign nearby colliders | off | Also gives each cloth the avatar's own colliders it could swing into, so a tail that passed through the leg in VRChat collides with it here. Improves on the original rather than copying it, so check before uploading |
ChilloutVR's own contact system is a near-exact superset of VRChat's — same shapes plus Box, the
same allowSelf / allowOthers / localOnly / collision tags under the same names, and receiver
types covering Constant, OnEnter and three kinds of Proximity. It lives inside the game client and
the CCK ships no way to author it, so converters have always had to approximate it with pointers
and triggers.
AvatarBridge can author it directly. Turn on Use ChilloutVR's native contacts under Advanced, and VRChat contacts convert one to one:
- Real proximity, rather than a distance-driven stand-in
- Collision tags kept verbatim, so contacts still meet other avatars' on the same names
localOnlyhonoured — the legacy path has nowhere to put it- No sync cost at all. Contacts sit on ChilloutVR's avatar whitelist rather than its local-only one, so every client simulates them for every avatar and reproduces the value rather than replicating it
It works by declaring the components locally: an asset bundle carries no script assemblies, only a
record of each script's assembly, namespace and class, which the player resolves against its own.
The declarations are generated into AvatarBridge/Runtime on import and removed automatically if a
future CCK provides the real thing.
✅ This works. Confirmed in a live ChilloutVR instance: CCK validation clean, avatar uploaded, contacts triggered by other players, and ChilloutVR's own runtime gizmos drawing the components — which is the proof that counts, because it means the game's real implementation is running against declarations generated here.
It is still off by default, and stays that way until more than one avatar has confirmed it. Turn it on deliberately, test in game, and keep the legacy path in mind as the fallback — the conversion switches to it by itself if anything is wrong.
⚠️ If a conversion ever leaves brokenContact_*components behind, delete them and reopen the scene before converting again. Unity manufactures a placeholder script for a component whose reference is dangling, and that placeholder then captures every new component of the same class — so one bad conversion quietly poisons the next. AvatarBridge detects this and refuses rather than producing another broken avatar, and Tools → Avatar Bridge → Diagnose native contacts will tell you exactly what Unity is holding.
The two platforms don't render VR the same way, and this is one of the few places that difference reaches your avatar. ChilloutVR renders single-pass instanced; VRChat renders double-wide single-pass. Both SDKs force their own mode, unconditionally.
Under VRChat's double-wide mode a shader gets both eyes without having to ask for them. Under instancing it has to declare that it knows which eye it's drawing — so a shader that never opted in looked perfectly fine in VRChat and draws into one eye only in ChilloutVR. Nobody did anything wrong; it's a conversion problem, which makes it worth fixing here.
The CCK flags these as potentially non-SPI; AvatarBridge reports them too, and can fix the ones that are fixable mechanically.
Turn on Patch non-SPI shaders for VR in Advanced. For each affected shader it writes a
patched copy into RehomedAssets next to your converted avatar, adds the stereo macros, and
points this avatar's materials at the copy.
- Your original shader is never modified, and neither is the original material — both are copied. Other avatars sharing them are unaffected. (Those shaders usually aren't yours.)
- A copy that doesn't compile is thrown away and the original left in place, so the worst case is a line in the report rather than wrong pixels.
- Not everything can be patched. Surface shaders have no vertex stage to edit, locked or generated shaders can't be parsed, and structs living in a shared include can't be edited from one file. Those are listed in the report instead, for hand-fixing or replacing.
- There's nothing to undo. The macros are the mode-agnostic ones — they expand to real instancing code under ChilloutVR's mode and to nothing under VRChat's or on desktop. So the patched copy is still a correct shader everywhere; it just also works here.
✅ This works. Confirmed in a live ChilloutVR instance: a soft-particle effect that the CCK flagged as non-SPI was patched, validated clean, uploaded, and renders correctly in both eyes in game.
It is still off by default, on one avatar's evidence. Compilation is the only thing that can be checked automatically — whether the result looks right is a judgement no editor script can make, so turn it on deliberately and check the effect in both eyes.
Passing the CCK's check isn't the same as being correct. It looks for four macros; a shader can have all four and still be broken — a soft-particle shader reading
_CameraDepthTexturethroughsampler2D/tex2Dprojis the common case, since that texture is an array under single-pass instanced. AvatarBridge rewrites that pair as well.
VRCFury bakes every menu parameter as a float, whatever it really is. Harmless in VRChat;
not here, because ChilloutVR writes a menu value using the entry's own declared type — write a
Bool into a Float parameter and nothing happens. That's the most common cause of "the toggle does
nothing in game".
So each parameter is retyped from what the avatar's logic says it is: the menu control it
drives (Toggle → bool, Dropdown → int, Slider → float), or for parameters with no control, how the
animator compares it. Anything read as a quantity — blend tree, motion time, or written by
an animation clip — stays float, and is named in the report so you can see the tool declined
rather than missed it.
Pick one in the Face tracking dropdown. Both options first strip whatever FT rig the avatar shipped with, so nothing fights over the same blendshapes.
- Native CVR Component — sets up
CVRFaceTrackingand maps the shapes. Self-contained, but the built-in solver is a bit stiff. - Unity Animator Blendtrees (DSR) — injects DragonSkyRunner's CVR Eye & Face Tracking rig (bundled, no separate import), repaths every clip onto your actual eye bones and face mesh, and reconciles its shape vocabulary against whatever your mesh has — matching by name, casing, ARKit ↔ Unified Expressions aliases, and combined/split rules. So an ARKit avatar works without renaming anything. Smoother and more expressive.
- None.
Either mode needs a tracking source at runtime — true of any CVR face-tracking avatar. Run VRCFaceTracking, and set CVR's Eye Tracking and Mouth Tracking modules to OSC.
Without the VRChat SDK there's no VRChat data to read, so conversion isn't possible — a VRChat
avatar's components won't even deserialize. Instead the tool prepares any humanoid for
ChilloutVR: CVRAvatar with viewpoint and voice position, viseme and blink detection, face
tracking, and the height scaler. Useful for a Booth model or an original avatar.
Why there's no VRChat SDK stub
A GUID-matching stub (like the DynamicBone one) could recover simple components, but it could never run VRCFury or NDMF — those are real code needing the real SDK — so Fury avatars would silently convert to empty shells. Unity also deserializes by field name and silently defaults what it can't match, so every SDK update would quietly change the output. The DynamicBone stub exists to work around a paywall; the VRChat SDK is free and one click.
Don't expect a working quad right now. The conversion completes and the report comes back clean, but the avatar holds its rest pose in game with only the IK-tracked parts following you. Several real bugs were found and fixed chasing this and none of them were the cause, so it is parked rather than solved.
What is known, from reading ChilloutVR's own code:
GrounderVRIKis deleted on load. ChilloutVR whitelists components per-avatar and destroys the rest silently. Worlds are allowed 57 FinalIK types, avatars 13.VRIK,LookAtIK,TwistRelaxer,GrounderIK,GrounderBipedIK,CCDIK,FABRIK,AimIKandLimbIKsurvive;GrounderVRIK,GrounderQuadruped,GrounderFBBIK,ArmIK,LegIKandFingerRigdo not. The report now names these.GrounderIKis not a substitute forGrounderVRIK. The first drives separate per-leg IK components; the second feeds position offsets into VRIK's own solver from inside its update callbacks. Swapping them gives no grounding at all, and ChilloutVR has no native foot placement to fall back on.- ChilloutVR always installs its own
VRIK. It destroys whatever is on the avatar's animator object, adds its own, and auto-detects references from the humanoid rig. On a quadruped rigged as humanoid that means a biped solve running over a quad rig, and nothing a converter does can prevent it. This is the current best guess at the root cause; it is not proven.
Tracking control does convert correctly — VRCAnimatorTrackingControl becomes ChilloutVR's
BodyControl, which is what hands a limb from IK over to animation — so the groundwork is there
if this gets picked up again. Bipeds are unaffected by any of it.
Not converted:
- Eye look / gaze — only blink transfers; set gaze up under Eye Look Settings on the
CVRAvatar. (Blendshape face tracking is handled.) - PhysBone posing, stretch & squish and their
_Stretch/_Squish/_IsPosedparameters. - VRC state behaviours other than Parameter Driver — removed and counted.
- Synced animator layers and ONSP audio.
- Content tags — set CVR's Advanced Tagging (NSFW, loud audio…) yourself before uploading.
- VRChat-only rendering — SPS/TPS deformation and anything else that needs VRChat's own shader systems. The meshes and materials survive; the effect doesn't.
Converted with caveats:
- Action-layer emotes. Only Gesture and FX convert by default — Base, Additive and Action are off, because CVR drives locomotion and emotes itself and merging VRChat's versions fights it. You can tick Action on, but its states rely on VRChat's emote flow and may simply be unreachable.
- Constant contact receivers reset to 0 when any pointer exits — CVR triggers don't count occupants.
- Stacked PhysBones (several chains on one bone that VRChat toggles between) all convert, but only one is left driving the chain — two solvers on the same bones jitter rather than blend. Nothing is deleted, so switching variant is one checkbox; the report names the one kept.
- Stacked same-type constraints merge into one — Unity and CVR allow only one per type per object, so the second's offsets are dropped (its sources are kept).
- Dropdowns sometimes keep
(unused)entries. CVR selects dropdown options by position, so gaps need padding. These are normally removed by renumbering the parameter, but that's unsafe when the value is used as a quantity or passed to a driver — the report says which applied. - Parameter-packing optimisers (
MemOpt_*and similar) leave odd-looking entries in your menu.⚠️ Don't delete them. They're what carries your toggles to other players. (An earlier version of this note said CVR can't sync a parameter without a menu entry — that's wrong. Syncing comes from the animator declaration; the menu entry decides whether the value is remembered in your avatar profile between loads. Still worth keeping.) - Shaders aren't translated. Poiyomi etc. work as-is, and VRCFury-baked materials are rescued out of Fury's temp folder so they don't render pink. Shaders that don't support VR stereo are a separate problem, below.
- Merged layers can fight CVR's locomotion on rare avatars, because VRChat keeps FX on its own playable layer and CVR has no equivalent. If an FX layer is overriding your pose, Mask merged layers off the humanoid rig under Advanced restores that separation. Off by default — the one case that prompted it turned out to be a different bug.
Hit Report an issue in the AvatarBridge window — it opens a pre-filled GitHub issue with your versions and detected packages already in it.
Two things make a report solvable immediately:
-
Attach
ConversionReport.mdfromAssets/AvatarBridge/Output/<avatar>/. Nearly every bug fixed so far was diagnosed from this file. -
Attach the right log:
Symptom Log Conversion errors, or wrong result in Unity Unity console text or Editor.logAvatar misbehaves or won't load in ChilloutVR %USERPROFILE%\AppData\LocalLow\ChilloutVR\ChilloutVR\Player.logA clean Unity log says nothing about an in-game failure. That's exactly how the "Error robot" bug was found — the Unity log was spotless.
Please re-run on the latest release first. Logs contain your project's file paths (and CVR logs your display name) — skim and redact if you'd rather not post them.
Quick questions can go to mrtactical on Discord. Anything reproducible is better as a
GitHub issue — those get tracked, linked to a fix and closed with a release.
- Gesture tables, CVR core parameters and several conversion patterns were studied from vrc3cvr (MIT) and the Narazaka fork.
- Gesture mapping and the Parameter Stream approach follow the official ChilloutVR references.
- The DynamicBone gravity split mirrors PhysBone-to-DynamicBone.
- MagicaCloth2 usage follows the official runtime construction docs; chain presets are MagicaCloth2's own.
- VRCFury avatars are baked by VRCFury's own builder — no Fury code is bundled and there's no hard dependency.
- The CVR VRCFT face-tracking rig is DragonSkyRunner's
CVR Eye & Face Tracking,
bundled under
Assets/AvatarBridge/FaceTrackingand redistributed with the author's permission. All rights remain theirs; if you reuse it, credit them. (Their upstream repo carries no explicit license file — aLICENSEthere would make the redistribution terms unambiguous.) - GrabbyBones is an optional third-party mod AvatarBridge targets but does not bundle.
- The avatar scaler's constant-speed smoothing is built on JustSleightly's Controller Templates; those clips are bundled under fresh GUIDs to avoid clashing with the original package. Credit for the technique remains theirs.
MIT — see LICENSE.md.