Development of Metaverse Avatar System
An avatar in a metaverse is not just a profile picture. It is an on-chain identifier, an interoperable digital object that a user carries from one application to another. Avatar system development is an intersection of 3D rendering, NFT standards, on-chain storage, and cross-chain identity. Below is how this is built from the inside.
Avatar Data Storage Architecture
On-chain vs off-chain: What to Store Where
The first architectural decision is what lives on-chain and what doesn't. Storing a full 3D model on the blockchain makes no economic sense: one GLB file weighs 2–20 MB, a transaction with such data would cost thousands of dollars on Ethereum mainnet.
Standard scheme:
| Layer | What is Stored | Where Stored |
|---|---|---|
| On-chain | Token ID, owner, trait hash, metadata URI | ERC-721 / ERC-1155 contract |
| Decentralized storage | JSON metadata, textures, base model | IPFS / Arweave |
| Centralized CDN | Optimized LOD versions, animations | AWS S3 + CloudFront |
| Runtime | Active modifications, session cosmetics | Game server / Redis |
Trait hash is keccak256 of a JSON object with avatar characteristics. It is stored on-chain and allows verification that off-chain data has not been substituted.
struct AvatarTraits {
bytes32 traitsHash; // keccak256 from traits JSON
uint16 bodyType; // enum: slim/athletic/heavy
uint16 skinTone; // 0-255
uint32 equippedItems; // bitmap of equipped NFTs
address customization; // address of customization contract
}
mapping(uint256 => AvatarTraits) public avatarData;
Metadata Standard: ERC-721 Extension
Basic ERC-721 metadata standard (name, description, image, attributes) is insufficient for a full avatar. An extension is needed, compatible with OpenSea but containing 3D-specific fields:
{
"name": "Avatar #4721",
"description": "...",
"image": "ipfs://Qm.../preview.png",
"animation_url": "ipfs://Qm.../avatar.glb",
"external_url": "https://metaverse.example/avatar/4721",
"attributes": [...],
"avatar_data": {
"version": "1.2",
"base_model": "ipfs://Qm.../base_athletic.glb",
"rig": "mixamo_compatible",
"textures": {
"albedo": "ipfs://Qm.../skin_albedo.png",
"normal": "ipfs://Qm.../skin_normal.png"
},
"vrm_url": "ipfs://Qm.../avatar.vrm",
"ready_player_me_id": "optional_rpm_id"
}
}
The animation_url field with GLB/VRM file is what allows OpenSea and other marketplaces to render 3D preview directly in the interface.
Customization and Equipment System
Modular Avatar Architecture
The base avatar model is built on the principle of slots: body, head, hair, top, bottom, shoes, accessories. Each slot can be filled with an NFT from different collections — provided they conform to the compatibility standard.
Smart contract level:
contract AvatarEquipment {
mapping(uint256 => mapping(uint8 => EquippedItem)) public equipment;
// avatarId => slotId => item
struct EquippedItem {
address nftContract;
uint256 tokenId;
uint8 slot;
}
function equip(
uint256 avatarId,
address nftContract,
uint256 itemTokenId,
uint8 slot
) external {
require(ownerOf(avatarId) == msg.sender, "Not owner");
require(
IERC721(nftContract).ownerOf(itemTokenId) == msg.sender,
"Don't own item"
);
require(
IWearable(nftContract).isCompatible(slot),
"Incompatible slot"
);
equipment[avatarId][slot] = EquippedItem(nftContract, itemTokenId, slot);
emit ItemEquipped(avatarId, nftContract, itemTokenId, slot);
}
}
The IWearable interface is a compatibility standard that all NFT clothing/accessory collections in the ecosystem should implement. Without it, you get isolated content islands that don't work together.
VRM and Ready Player Me Integration
VRM (Virtual Reality Model) is an open standard for humanoid 3D avatars, based on glTF 2.0. Supported in VRChat, cluster, Resonite, and many other platforms. If you're building an avatar system designed for interoperability — VRM is the base format.
Ready Player Me provides an SDK for avatar generation and customization with a ready-made web editor. Integration via their API allows embedding their editor in your metaverse in days, but creates a third-party service dependency. Good solution for MVP, requires replacement when scaling.
Our recommended production stack:
- Three.js / React Three Fiber — browser rendering
- @pixiv/three-vrm — VRM parsing and rendering in Three.js
- Mixamo — rigging and animations (can be transferred to any rigged mesh)
- Draco compression — GLB geometry compression (60-80% size reduction)
Interoperability and Cross-Platform Identity
Interoperability Problem
Theoretically, if an avatar is an NFT, it should work everywhere. In practice, Decentraland, The Sandbox, and Roblox use different formats, different model proportions, different skeleton systems. An avatar from one platform cannot be directly used in another without conversion.
Partial solution — Open Metaverse Interoperability (OMI) Group standards and Metaverse Standards Forum. As of 2024-2025, there is no complete standardization, but there are working approaches:
- Store canonical VRM version of avatar as master
- When importing into platform — convert via adapter (server-side or client-side)
- Describe bone and slot mapping in avatar metadata
- Use morph targets to adapt proportions
ENS and Decentralized Identity
Several approaches are used to link avatar with on-chain identity:
ENS (Ethereum Name Service) + text records: user specifies their avatar NFT token ID in ENS records. Applications resolve ENS → get avatar → render.
avatar.vitalik.eth = eip155:1/erc721:0xAbC...123/4721
This format (CAIP-19) is standardized and supported by a growing number of protocols.
Lens Protocol stores avatar as part of profile — NFT-based social graph with avatar metadata support at the protocol level.
Ceramic Network / DID — decentralized identity documents, where avatar is one of the fields in the user's profile.
Animations and Behavior System
Animation Systems
Avatar animations are divided into three categories:
Base animations (idle, walk, run, jump) — provided with the system and work with all avatars through a rigged skeleton.
Emotions and gestures — can be NFT assets. User buys a "rare dance" as an NFT and it appears in their gesture library. Technically — a separate animation clip file + on-chain record of usage rights.
Procedural animations — IK (Inverse Kinematics) for environmental interaction: picking up objects, sitting on surfaces, reacting to physics. Implemented via three.js + ready-made IK solvers (three-ik, fabrik).
Multiplayer Synchronization
Real-time animation synchronization is one of the most complex tasks. Stack for WebSocket-based metaverse:
- State compression: transmit not full transform, but delta + quaternion rotation
- Interpolation: client interpolates between received states (lerp/slerp)
- Dead reckoning: position prediction on packet loss
- Priority queue: closer avatars receive more frequent updates
Protocol: WebRTC data channels for P2P (small instances), WebSocket through server for large. Format: binary (MessagePack or FlatBuffers), not JSON — difference in load is 3-5x.
Avatar Economics and Monetization
NFT Monetization Layers
Avatar system opens several monetization levels:
Base avatars — collection of basic avatars (PFP-style), generation via traits-based algorithm. Standard ERC-721 mint with royalty via ERC-2981.
Wearables marketplace — NFT clothing and accessories from first and third parties. Creator royalty = portion from each sale.
Animation passes — subscription or one-time purchase for access to animation library.
Avatar rentals — ERC-4907 (rentable NFT) allows avatar owner to rent it out over time. User role = temporary user without transfer rights.
// ERC-4907
function setUser(uint256 tokenId, address user, uint64 expires) external {
require(ownerOf(tokenId) == msg.sender, "Not owner");
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
emit UpdateUser(tokenId, user, expires);
}
This mechanism is particularly interesting for games where avatar = leveled character: renting high-level characters — a separate market.
Technical Stack and Implementation Recommendations
For a project from scratch, we recommend the following stack:
| Component | Technology | Rationale |
|---|---|---|
| Smart contracts | Solidity + OpenZeppelin | ERC-721 + extensions |
| Metadata storage | IPFS + Pinata/NFT.Storage | Decentralization + CDN |
| 3D render | React Three Fiber + drei | React compatibility |
| VRM support | @pixiv/three-vrm | Only mature VRM parser |
| Animations | Mixamo → Three.js AnimationMixer | Wide library |
| Realtime sync | Colyseus (Node.js game server) | WebSocket + state sync |
| Customizer | Three.js + custom UI | Full UX control |
| Chain | Polygon / Arbitrum | Low gas for mint/equip operations |
Main advice from practice: don't try to build full interoperability from the first version. Start with VRM as internal format, provide quality rendering within your platform, and add adapters for other platforms iteratively. Architecture should allow this — hence the importance of standardized metadata format from the start.







