Claude-skill-registry 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
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/avatar-ugc" ~/.claude/skills/majiayu000-claude-skill-registry-avatar-ugc && rm -rf "$T"
manifest: skills/data/avatar-ugc/SKILL.md
source content

Avatar & UGC Customization

Quick Reference Links

Official Documentation:

Wiki References:


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

PropertyTypeDescription
Shirt
numberShirt asset ID
Pants
numberPants asset ID
GraphicTShirt
numberT-shirt asset ID
Face
numberFace asset ID
Head
numberHead mesh asset ID
Torso
numberTorso mesh asset ID
LeftArm
,
RightArm
numberArm mesh asset IDs
LeftLeg
,
RightLeg
numberLeg mesh asset IDs
HatAccessory
stringComma-separated hat asset IDs
HairAccessory
stringComma-separated hair asset IDs
FaceAccessory
stringComma-separated face accessory IDs
NeckAccessory
stringComma-separated neck accessory IDs
ShouldersAccessory
stringComma-separated shoulder accessory IDs
FrontAccessory
stringComma-separated front accessory IDs
BackAccessory
stringComma-separated back accessory IDs
WaistAccessory
stringComma-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 ValueDescription
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

  1. Always wrap asset loading in
    pcall()
    for error handling
  2. Cache HumanoidDescriptions when making multiple changes
  3. Use
    GetAppliedDescription()
    to preserve existing appearance when making partial changes
  4. 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