Agent skill
avatar-ugc
Programmatically apply UGC (User-Generated Content) to characters including clothing, accessories, and avatar customization. Use when dressing NPCs, building avatar editors, or creating character customization systems.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/avatar-ugc
SKILL.md
Avatar & UGC Customization
Quick Reference Links
Official Documentation:
- Character Appearance - Overview of avatar customization
- HumanoidDescription API - Core avatar properties
- Shirt API - Classic shirt clothing
- Pants API - Classic pants clothing
- ShirtGraphic API - T-shirts
- Accessory API - Accessories/hats
- InsertService API - Loading assets
- AvatarEditorService API - Avatar editor support
Wiki References:
- User-generated content (Wiki) - UGC overview
- Accessory (Wiki) - Accessory system
- Clothing (Wiki) - Clothing system
Two Approaches to Avatar Customization
1. HumanoidDescription (Recommended)
Best for: Comprehensive avatar changes, player characters, loading from asset IDs
2. Direct Clothing Instances
Best for: NPCs, simple clothing changes, runtime modifications
HumanoidDescription Approach
Key Properties
| Property | Type | Description |
|---|---|---|
Shirt |
number | Shirt asset ID |
Pants |
number | Pants asset ID |
GraphicTShirt |
number | T-shirt asset ID |
Face |
number | Face asset ID |
Head |
number | Head mesh asset ID |
Torso |
number | Torso mesh asset ID |
LeftArm, RightArm |
number | Arm mesh asset IDs |
LeftLeg, RightLeg |
number | Leg mesh asset IDs |
HatAccessory |
string | Comma-separated hat asset IDs |
HairAccessory |
string | Comma-separated hair asset IDs |
FaceAccessory |
string | Comma-separated face accessory IDs |
NeckAccessory |
string | Comma-separated neck accessory IDs |
ShouldersAccessory |
string | Comma-separated shoulder accessory IDs |
FrontAccessory |
string | Comma-separated front accessory IDs |
BackAccessory |
string | Comma-separated back accessory IDs |
WaistAccessory |
string | Comma-separated waist accessory IDs |
Creating HumanoidDescription
-- Method 1: Create new empty description
local humanoidDescription = Instance.new("HumanoidDescription")
-- Method 2: Get from existing character
local function getDescriptionFromCharacter(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
return humanoid:GetAppliedDescription()
end
return nil
end
-- Method 3: Get from player's avatar
local Players = game:GetService("Players")
local function getDescriptionFromUserId(userId)
local success, description = pcall(function()
return Players:GetHumanoidDescriptionFromUserId(userId)
end)
return success and description or nil
end
-- Method 4: Get from outfit ID
local function getDescriptionFromOutfit(outfitId)
local success, description = pcall(function()
return Players:GetHumanoidDescriptionFromOutfitId(outfitId)
end)
return success and description or nil
end
Modifying HumanoidDescription
local function customizeDescription(humanoidDescription)
-- Set clothing (use asset IDs, not content URLs)
humanoidDescription.Shirt = 6536023867 -- Shirt asset ID
humanoidDescription.Pants = 6536027646 -- Pants asset ID
humanoidDescription.GraphicTShirt = 1711661 -- T-shirt asset ID
-- Set accessories (comma-separated strings for multiple)
humanoidDescription.HatAccessory = "2551510151,2535600138"
humanoidDescription.HairAccessory = "4819740796"
humanoidDescription.FaceAccessory = ""
humanoidDescription.BackAccessory = "4447084948"
-- Set body parts
humanoidDescription.Face = 86487700
humanoidDescription.Head = 0 -- 0 = default
-- Set body colors
humanoidDescription.HeadColor = Color3.fromRGB(234, 184, 146)
humanoidDescription.TorsoColor = Color3.fromRGB(234, 184, 146)
humanoidDescription.LeftArmColor = Color3.fromRGB(234, 184, 146)
humanoidDescription.RightArmColor = Color3.fromRGB(234, 184, 146)
humanoidDescription.LeftLegColor = Color3.fromRGB(234, 184, 146)
humanoidDescription.RightLegColor = Color3.fromRGB(234, 184, 146)
-- Set scale
humanoidDescription.HeightScale = 1.0
humanoidDescription.WidthScale = 1.0
humanoidDescription.HeadScale = 1.0
humanoidDescription.BodyTypeScale = 0.0 -- 0 = blocky, 1 = realistic
humanoidDescription.ProportionScale = 0.0
return humanoidDescription
end
Setting Multiple Accessories with SetAccessories
local function setMultipleAccessories(humanoidDescription)
local accessories = {
{
Order = 1,
AssetId = 6984769289,
AccessoryType = Enum.AccessoryType.Hat
},
{
Order = 2,
AssetId = 6984767443,
AccessoryType = Enum.AccessoryType.Hair
},
{
Order = 3,
AssetId = 4447084948,
AccessoryType = Enum.AccessoryType.Back
}
}
humanoidDescription:SetAccessories(accessories, false) -- false = don't include rigid accessories
end
Applying HumanoidDescription
-- Apply to existing character
local function applyToCharacter(character, humanoidDescription)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid:ApplyDescription(humanoidDescription)
end
end
-- Apply to NPC
local function dressNPC(npc, shirtId, pantsId, hatIds)
local humanoid = npc:FindFirstChildOfClass("Humanoid")
if not humanoid then return end
local description = humanoid:GetAppliedDescription()
description.Shirt = shirtId or description.Shirt
description.Pants = pantsId or description.Pants
description.HatAccessory = hatIds or description.HatAccessory
humanoid:ApplyDescription(description)
end
-- Load character with description (for spawning)
local function spawnWithDescription(player, humanoidDescription)
player:LoadCharacterWithHumanoidDescription(humanoidDescription)
end
Direct Clothing Instances (Classic Method)
Apply Shirt to Character/NPC
local function applyShirt(character, shirtAssetId)
-- Find or create Shirt instance
local shirt = character:FindFirstChildOfClass("Shirt")
if not shirt then
shirt = Instance.new("Shirt")
shirt.Parent = character
end
-- Set template (use rbxassetid:// format)
shirt.ShirtTemplate = "rbxassetid://" .. shirtAssetId
end
Apply Pants to Character/NPC
local function applyPants(character, pantsAssetId)
local pants = character:FindFirstChildOfClass("Pants")
if not pants then
pants = Instance.new("Pants")
pants.Parent = character
end
pants.PantsTemplate = "rbxassetid://" .. pantsAssetId
end
Apply T-Shirt to Character/NPC
local function applyTShirt(character, tshirtAssetId)
local tshirt = character:FindFirstChildOfClass("ShirtGraphic")
if not tshirt then
tshirt = Instance.new("ShirtGraphic")
tshirt.Parent = character
end
tshirt.Graphic = "rbxassetid://" .. tshirtAssetId
end
Apply Complete Outfit
local function applyOutfit(character, outfit)
-- outfit = { shirt = id, pants = id, tshirt = id }
if outfit.shirt then
applyShirt(character, outfit.shirt)
end
if outfit.pants then
applyPants(character, outfit.pants)
end
if outfit.tshirt then
applyTShirt(character, outfit.tshirt)
end
end
-- Usage
applyOutfit(npc, {
shirt = 6536023867,
pants = 6536027646,
tshirt = 1711661
})
Loading Accessories with InsertService
local InsertService = game:GetService("InsertService")
-- IMPORTANT: Can only load assets accessible to experience creator
local function loadAccessory(accessoryAssetId)
local success, model = pcall(function()
return InsertService:LoadAsset(accessoryAssetId)
end)
if success and model then
local accessory = model:FindFirstChildOfClass("Accessory")
if accessory then
accessory.Parent = nil -- Remove from model container
model:Destroy()
return accessory
end
model:Destroy()
end
return nil
end
-- Apply loaded accessory to character
local function giveAccessory(character, accessoryAssetId)
local accessory = loadAccessory(accessoryAssetId)
if accessory then
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid:AddAccessory(accessory)
end
end
end
AvatarEditorService (Catalog Integration)
Get Item Details
local AvatarEditorService = game:GetService("AvatarEditorService")
local function getItemDetails(assetId)
local success, details = pcall(function()
return AvatarEditorService:GetItemDetails(assetId, Enum.AvatarItemType.Asset)
end)
if success then
return {
name = details.Name,
assetType = details.AssetType,
price = details.Price,
isOwned = details.IsOwned,
isFavorited = details.IsFavorited,
creatorId = details.CreatorTargetId,
creatorName = details.CreatorName
}
end
return nil
end
Get Player's Inventory
local function getPlayerInventory(player, assetTypes)
-- assetTypes = array of Enum.AvatarAssetType
local success, inventory = pcall(function()
return AvatarEditorService:GetInventory(assetTypes)
end)
if success then
return inventory:GetCurrentPage()
end
return {}
end
-- Example: Get player's hats
local hatTypes = {Enum.AvatarAssetType.Hat}
local playerHats = getPlayerInventory(player, hatTypes)
Get Outfit Details
local function getOutfitDetails(outfitId)
local success, details = pcall(function()
return AvatarEditorService:GetOutfitDetails(outfitId)
end)
return success and details or nil
end
Complete NPC Dressing System
local Players = game:GetService("Players")
local NPCOutfits = {
Guard = {
shirt = 6536023867,
pants = 6536027646,
hats = "2551510151",
bodyColors = {
head = Color3.fromRGB(234, 184, 146),
torso = Color3.fromRGB(234, 184, 146)
}
},
Merchant = {
shirt = 398633610,
pants = 398635927,
tshirt = 0,
hats = "4819740796"
}
}
local function createDressedNPC(npcModel, outfitName)
local outfit = NPCOutfits[outfitName]
if not outfit then return end
local humanoid = npcModel:FindFirstChildOfClass("Humanoid")
if not humanoid then return end
-- Get current description or create new one
local description = humanoid:GetAppliedDescription()
-- Apply outfit
if outfit.shirt then
description.Shirt = outfit.shirt
end
if outfit.pants then
description.Pants = outfit.pants
end
if outfit.tshirt then
description.GraphicTShirt = outfit.tshirt
end
if outfit.hats then
description.HatAccessory = outfit.hats
end
-- Apply body colors
if outfit.bodyColors then
if outfit.bodyColors.head then
description.HeadColor = outfit.bodyColors.head
end
if outfit.bodyColors.torso then
description.TorsoColor = outfit.bodyColors.torso
end
-- ... etc for other body parts
end
-- Apply the description
humanoid:ApplyDescription(description)
end
UGC Catalog Search (Server-Side)
local AvatarEditorService = game:GetService("AvatarEditorService")
-- Search catalog for items
local function searchCatalog(keyword, assetType, sortType)
local searchParams = CatalogSearchParams.new()
searchParams.SearchKeyword = keyword
searchParams.AssetTypes = {assetType}
searchParams.SortType = sortType or Enum.CatalogSortType.Relevance
local success, results = pcall(function()
return AvatarEditorService:SearchCatalog(searchParams)
end)
if success then
return results:GetCurrentPage()
end
return {}
end
-- Example: Search for hats
local hats = searchCatalog("crown", Enum.AvatarAssetType.Hat)
for _, hat in ipairs(hats) do
print(hat.Name, hat.Id)
end
AccessoryType Enum Reference
| Enum Value | Description |
|---|---|
Enum.AccessoryType.Hat |
Hats, helmets |
Enum.AccessoryType.Hair |
Hair styles |
Enum.AccessoryType.Face |
Glasses, masks |
Enum.AccessoryType.Neck |
Necklaces, scarves |
Enum.AccessoryType.Shoulder |
Shoulder pads |
Enum.AccessoryType.Front |
Front accessories |
Enum.AccessoryType.Back |
Backpacks, wings |
Enum.AccessoryType.Waist |
Belts, tails |
Enum.AccessoryType.TShirt |
Layered t-shirts |
Enum.AccessoryType.Shirt |
Layered shirts |
Enum.AccessoryType.Pants |
Layered pants |
Enum.AccessoryType.Jacket |
Layered jackets |
Enum.AccessoryType.Sweater |
Layered sweaters |
Enum.AccessoryType.Shorts |
Layered shorts |
Enum.AccessoryType.LeftShoe |
Left shoe |
Enum.AccessoryType.RightShoe |
Right shoe |
Enum.AccessoryType.DressSkirt |
Dresses/skirts |
Important Considerations
Asset ID vs Content ID
- Asset ID: The numeric ID (e.g.,
6536023867) - Content ID: The full URL format (e.g.,
rbxassetid://6536023867) - HumanoidDescription properties use Asset IDs (numbers)
- Shirt/Pants/ShirtGraphic templates use Content IDs (strings)
Security & Permissions
InsertService:LoadAsset()only works with assets accessible to the experience creator- For UGC items from other creators, use HumanoidDescription with asset IDs instead
- Player inventory access requires appropriate permissions
Classic vs Layered Clothing
- Classic Clothing: Shirt, Pants, ShirtGraphic (2D textures)
- Layered Clothing: 3D clothing that deforms with the avatar body
- Layered clothing uses AccessoryType values like TShirt, Jacket, Sweater
Best Practices
- Always wrap asset loading in
pcall()for error handling - Cache HumanoidDescriptions when making multiple changes
- Use
GetAppliedDescription()to preserve existing appearance when making partial changes - For NPCs, apply description once after all changes rather than multiple times
Checklist for Avatar Customization
- Determine approach (HumanoidDescription vs Direct Instances)
- Collect asset IDs for clothing/accessories
- Handle pcall for all async operations
- Test with R6 and R15 rigs
- Consider body colors and scale
- Verify asset permissions for InsertService usage
- Cache descriptions for performance
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?