§ How to · with Picoo
How to make character customization in Roblox
By Sametcan Tasgiran, Founder & Developer·Published ·Updated
Roblox already has an API that applies an entire look in one call — most customization code is a hand-rolled version of it that handles fewer cases.
HumanoidDescription, the server/client split, and the rig-type assumption that quietly breaks half of the scripts people copy.
HumanoidDescription-based
A whole look — body colours, clothing, accessories, scale — applied in one call.
Server-applied
Appearance set where it replicates, instead of a skin only the wearer can see.
Rig-safe
Works for the rig type your game actually uses rather than assuming R15.
Saved looks
The chosen skin persists and is reapplied on respawn, not just at first spawn.
Owned-only equipping
The server checks ownership before applying anything purchased.
Preview viewport
A rotating preview in the menu, driven by the same description that gets applied.
Files Picoo ships for this prompt
2 files · 190 lines · ~30s · 1 credit
SkinService
Description building, ownership checks, apply, persistence, respawn reapply.
115 lines
SkinMenu
Grid, ViewportFrame preview, equip requests.
75 lines
Sample output: ServerScriptService.SkinService
-- HumanoidDescription applies an ENTIRE look atomically: body colours, clothing,
-- accessories, scaling. Setting Shirt/Pants/BodyColors by hand works but misses
-- accessories and scale, which is why hand-rolled skin systems drift.
local function applySkin(plr: Player, skin: SkinConfig)
local char = plr.Character; if not char then return end
local hum = char:FindFirstChildOfClass("Humanoid"); if not hum then return end
local desc = hum:GetAppliedDescription() -- start from what they wear now
desc.Shirt = skin.shirtId
desc.Pants = skin.pantsId
desc.HeadColor = skin.skinColor
desc.LeftArmColor, desc.RightArmColor = skin.skinColor, skin.skinColor
desc.LeftLegColor, desc.RightLegColor = skin.skinColor, skin.skinColor
desc.TorsoColor = skin.skinColor
desc.HatAccessory = skin.hatIds -- comma-separated asset ids
-- MUST run on the server. Applied on the client it looks right to that one
-- player and to nobody else -- the classic "my skin works but nobody sees it".
hum:ApplyDescription(desc)
end
-- Respawn wipes appearance. Reapplying only at first spawn is why skins vanish
-- after the first death.
Players.PlayerAdded:Connect(function(plr)
plr.CharacterAppearanceLoaded:Connect(function()
local saved = Store.get(plr).equippedSkin
if saved then applySkin(plr, SKINS[saved]) end
end)
end)Building character customization in Roblox
Character customization is one of the few areas where Roblox hands you an API that is genuinely better than what most people build by hand.
That API is HumanoidDescription. It represents a complete look — clothing, body colours, accessories, scaling — as one object you can read from a character, modify, and apply back. The hand-rolled alternative is setting a Shirt and Pants instance and maybe some BodyColors, which works for the simple case and then quietly falls short: accessories are not handled, scale is not handled, and there is no way to read back what someone is currently wearing. Systems built that way accumulate special cases until they are a worse version of the thing that already existed.
The placement question is the usual one, and it produces a distinctive bug. Appearance applied on the client looks perfect to the player who applied it and unchanged to everyone else, so the developer tests alone, sees their skin, and ships. Applying on the server is what makes it real; the client requests, the server validates ownership and applies.
Respawn is the second reliable failure. A new character is built from the player's default appearance, so anything you set is discarded on death. Hooking the appearance-loaded signal rather than applying once at join is what makes a skin stick.
The last one catches people copying code: R6 versus R15. Your game's avatar type is a place setting, not a player choice, and a script written against R15 part names or scaling does nothing useful on an R6 rig. Either handle both or set the avatar type explicitly and only support one — the failure mode of ignoring it is a customization system that works for you and not for the players who joined a differently configured place.
See more on the Luau generator, the game builder, or browse the full blog.
Frequently asked
Why can only I see my custom skin?+
It was applied on the client. Appearance changed locally is not replicated, so it looks correct on the wearer's screen and unchanged to everyone else. Apply on the server — the client's job is to request a skin, not to wear one.
Why does my skin disappear when I die?+
A respawn builds a fresh character from the player's default appearance, so anything you applied is gone. Reapply on CharacterAppearanceLoaded rather than once at join, or every death resets the look.
HumanoidDescription or setting Shirt and Pants directly?+
HumanoidDescription for anything more than a shirt swap. It applies clothing, body colours, accessories and scaling atomically, and reads back the current look so you can modify rather than rebuild. Hand-set properties work but tend to miss accessories and scale, which is where hand-rolled systems accumulate bugs.
Why does my customization break for some players?+
Usually an R6/R15 assumption. Scripts that reference R15 part names or use scaling properties do nothing sensible on an R6 rig, and your game's avatar type is a place setting players do not control. Check the rig type, or set the avatar type explicitly so you only have one case to support.
Why doesn't my shirt asset ID work?+
The number on the catalog page is the catalog item, not the template image the Shirt instance wants. Using the page ID gives you a blank shirt with no error. Take the template asset ID, and test one in Studio before building a whole catalogue on the wrong number.
Related Picoo prompts
Roblox shop system
5 files · 180 lines · 47 seconds · 1 credit. Robux + in-game currency, both server-validated.
Roblox inventory system
6 files · 230 lines · 56 seconds · 1 credit. 30-slot inventory + 5-slot hotbar.
Roblox emote system
4 files · 130 lines · 34 seconds · 1 credit. 8-slot wheel, configurable emotes.