Custom avatars and Unity packages
Read this page as Markdown · All docs for AI agents
Uploading a character
Open Character & effects → Your own avatar → Upload model while signed in to a registered account. Choose a .glb, .vrm or .unitypackage file. A valid conversion becomes your custom avatar. A rejected upload keeps your previous avatar and selection unchanged.
Your upload is converted into a self-contained GLB that other players download. For Unity packages, include one assembled character model and its supported textures. Upload characters and textures you have permission to distribute.
Exact upload and rendering limits
| Limit | Value |
|---|---|
| Original upload | 30,000,000 bytes, inclusive: decimal 30 MB |
| Attempts | 6 per registered player in a rolling 60-minute window |
| Failed conversions | Consume an attempt once admitted |
| Shared quota | GLB, VRM and Unity package uploads share the same six attempts |
| Converted downloadable avatar | 4,194,304 bytes, inclusive: 4 MiB |
| Expanded Unity archive | Smaller of 120,000,000 bytes and max(8,000,000, compressedBytes × 200) |
| Archive decompression time | 5 seconds |
| Archive entries / Unity assets | 4,096 / 1,024 |
| Models inside one package | One binary FBX, GLB or VRM, or one supported assembled text-mesh prefab |
| Meshes / material primitives | 48 / 96 |
| Mesh vertices / triangles | 150,000 / 70,000 |
| Vertices / triangles across placed mesh instances | 200,000 / 70,000 |
| Draw calls across placed mesh instances | 256 |
| Scenes | 4 |
| Scene nodes / hierarchy depth | 256 / 32 |
| Materials / textures | 32 / 16 |
| Skins / total bone references | 4 / 256; FBX requires one armature |
| Bone influences | At most four per vertex |
| Morph targets | 32 per primitive |
| Final texture size | At most 2,048 × 2,048 per texture |
| Final aggregate decoded textures | 4,194,304 pixels |
The 70,000-triangle limit is a hard rejection threshold. It counts repeated mesh instances and inactive objects, and is checked again before browser loading. Texture resizing never reduces or bypasses this triangle count.
The file-size, format and HTTP-encoding preflight may reject a request before charging an attempt. Once admitted, malformed files, failed conversions, interrupted uploads and unavailable conversion capacity can consume it. Successful uploads also consume it. The API returns HTTP 429 with an ISO retryAt timestamp when the rolling allowance is exhausted. Uploading another format or making concurrent requests does not reset the counter. Registered players have a separate allowance of one profile-picture attempt per rolling hour, including failed conversions.
Keep your final character within 4 MiB using modest geometry and compact texture atlases. Direct GLB/VRM source textures may total up to 64 megapixels, with at most 4,096 × 4,096 per image; the converter resizes and rewrites them to the final budgets. Duplicate images and unused emission maps are removed; eligible small color textures can share an atlas. Tiled or unsupported texture setups must fit the limits themselves. Strip unrelated assets before exporting a package to reduce its download and expansion size. If a package exceeds the expansion budget, export just the character and its textures, or upload a self-contained GLB/VRM.
What a Unity package can contain
Use an ordinary gzip-compressed Unity archive with GUID directories and pathname, asset, asset.meta and optional preview.png entries. Asset paths must be normal Assets/... paths. Links, traversal paths, duplicate entries, extended tar headers and nested archives are rejected. Files are never extracted using their supplied pathnames.
For binary FBX packages:
- Include one assembled character mesh/armature model, not multiple outfit or accessory FBX files.
- Include its static PNG, JPEG or WebP textures as separate package assets. Embedded FBX images and external filesystem paths are not loaded.
- Use unique texture basenames and unique material names. The converter can match an FBX texture reference to a package texture by basename, or a supported Unity material to a texture by its GUID.
- Keep source textures within 16,777,216 pixels in total. They are fully decoded, reduced proportionally to the final pixel/dimension budget, and rewritten as PNG before Blender receives them. Animated images are rejected.
- Text Unity
.matfiles may supply their material name,_MainTex/_BaseMaptexture GUID and_Color/_BaseColorRGBA values. For binary.matfiles, the converter uses a unique local texture GUID hint, the FBX's supported material data or a uniquely named package texture. Name the texture after the material:Body.matmatchesBody.pngor an older FBX export such asBody_00_00_00.jpg. Ambiguous matches are rejected. Binary material objects are never instantiated; shader settings are discarded. Untextured materials retain a plain color. - Materials are rebuilt using the platform's standard rendering. Custom shader source, shader graphs, shader plugins, normal-map setups, transparency tricks and other Unity material features are not imported by this FBX path.
For a packaged GLB or VRM, the model must already contain its images and binary data. Loose package textures do not fill missing external GLB references. The same strict GLB validation and final budgets apply as for a direct upload.
Assembled Unity prefabs backed by text Mesh .asset files are also supported. The converter reads only a fixed set of transforms, skin geometry, bone weights, bind poses and material references. Packages containing multiple standalone FBX/GLB models still require an assembled export.
Package scripts, plugins, shaders, scenes, controllers and SDK components are ignored; no Unity editor setup code is run. Export the assembled character with its intended clothing, dimensions and rig already applied. If your package requires an editor setup step, ask its author for an assembled GLB/VRM export.
Humanoid preparation in Blender
Use your trusted Blender installation and your own scene. Keep the character upright, with its feet near the floor and a consistent scale. Blender uses Z-up; its glTF exporter converts to Y-up. The runtime fits the character to Betworld's standard body height. Extremely flat, enormous, zero-height or malformed skeleton/mesh bounds are rejected.
The skinned mesh must belong to the active scene and have one unambiguous set of these required humanoid bones:
| Body part | Canonical node name |
|---|---|
| Hips | casino_pelvis |
| Spine | casino_spine1 |
| Head | casino_head |
| Upper arms | casino_upperarml, casino_upperarmr |
| Forearms | casino_forearml, casino_forearmr |
| Hands | casino_handl, casino_handr |
| Thighs | casino_thighl, casino_thighr |
| Calves | casino_calfl, casino_calfr |
| Feet | casino_footl, casino_footr |
Standard VRM humanoid metadata, common Mixamo bone names and common humanoid aliases are normalized to this vocabulary. This includes older FBX names such as L_UpperArm, R_Forearm, L_Thigh, Head1 and Neck1. A Spine → Spine1 → Spine2 chain maps its lower and middle joints to the spine and chest controls. Optional named chest, neck, toe and eye nodes can survive. Duplicate required bone mappings or missing required bones fail validation. Weight the mesh to its rig; merely naming unconnected objects is insufficient. Do not use more than four bone influences per vertex.
Bounded sparse accessors in source VRMs are expanded into dense data with range, index and allocation checks. Unsupported compressed geometry extensions are rejected. Compatible skin tables and unused vertices are compacted; duplicate joint influences from older exporters are merged and normalized before strict output validation. VRM mouth and blink presets are retained; unused authoring expressions and imported animation clips are removed.
VRM 0.x models are turned to match the game's forward direction; VRM 1.0 models already use it. Models that depend on VRM bone constraints are rejected with an export instruction. Bake those constraints into an ordinary humanoid rig before uploading; the importer does not run their limb drivers.
Betworld drives movement and emotes through the humanoid rig. Preview walking, arm raises, crouching and emotes; adjust shoulder weights and joint placement in your source model where needed. Uploaded animation clips and Unity animator setup are omitted.
Walking, running, jumping, crouching, swinging, sitting and the emote catalogue use Betworld's animations. The result depends on your avatar's joint placement and skin weights. Extra finger joints retain their imported pose unless they have a supported animation mapping; VRChat gesture controllers are not transferred. Mouth movement while speaking requires a recognized mouth shape such as vrc.v_aa, jawOpen or mouthOpen. A model without those shapes still supports body movement, but its mouth remains static.
Recognized facial names such as vrc.v_aa, vrc.v_oh, vrc.v_sil, vrc.blink_left and vrc.blink_right can survive conversion. Facial deformation must stay within the character bounds. Export your character without hats, helmets or other headwear.
After you upload
Clients display up to 12 custom-avatar instances using four distinct models on desktop, or six instances using two distinct models with touch controls. Additional custom avatars use the default character so a crowded room does not keep loading more texture sets. This limit is separate from your avatar's individual file and geometry budgets.
The upload panel shows transfer progress, elapsed conversion time and a cancel button. Requests have bounded deadlines, so a stalled request returns an error.
The importer checks the archive, converts supported model and texture data, then validates the resulting humanoid GLB in a second fresh sandbox. Only a successful result replaces your current custom avatar. Failed conversions discard their temporary files and preserve your previous avatar and selection.
Files upload directly through Cloudflare, which runs the converter and validator in separate disposable VMs. Temporary inputs and validated models use private R2 storage; only validated output is downloadable. The game server handles account authorization and model references, without receiving avatar file bytes. Large or unsupported avatars can still be rejected by the limits above.
Read the returned error before retrying. An admitted retry uses another upload attempt. For geometry or rig errors, correct the source model; for unsupported package setup, use an assembled GLB/VRM export.
HTTP reference for integrations
Avatar upload uses the player's normal authenticated website session. World MCP credentials do not authorize avatar uploads. World prop/game-skin uploads remain static GLB workflows with their own limits; they do not accept Unity packages.
POST https://betworld.gg/api/avatars/upload
- Body: the original file bytes, not JSON, base64 or multipart form data.
Content-Type: application/vnd.unityfor.unitypackage.Content-Type: model/gltf-binaryfor.glband.vrm.- No HTTP
Content-Encoding; a Unity package's own bounded gzip archive is decoded inside the sandbox. - Existing authentication, same-origin and registration checks apply.
- Success: JSON containing
player, final bytesizeand, for Unity packages, a compatibilitynotice. - Errors include
401unauthenticated,403registration/origin restriction,413oversized,415unsupported type/encoding,422invalid conversion,429quota exceeded and503converter unavailable.
GET /api/avatars/ returns the current model, maxBytes: 30000000, outputMaxBytes: 4194304 and uploadsPerHour: 6.
Do not automatically retry failed imports: each admitted retry can consume another attempt. Show the returned reason and help the creator correct their original asset. Prefer a direct self-contained GLB/VRM when the Unity package depends on unsupported setup.