Tutorial 2026-05-14

FiveM Character Creation Screen Development

TDYSKY

TDYSKY

Founder & Lead Developer at Agency Scripts

Why Character Creation Matters in Roleplay

Character creation is the very first interactive moment a player experiences on your FiveM roleplay server. It sets the tone for everything that follows. A polished, immersive character creator tells new players that your server is professionally built and worth investing their time in. A bare-bones or buggy one sends them straight to the server browser looking for alternatives. Beyond first impressions, the character creator defines the visual identity each player carries throughout their entire roleplay experience. It determines heritage blends, facial features, hair styles, clothing, and biographical details like name and date of birth. Getting this right means building a system that communicates with GTA V's native appearance system, presents the options through an intuitive NUI interface, persists selections to a database, and loads them back reliably every time the player spawns.

Understanding GTA V's Ped Appearance System

GTA V uses a heritage-based appearance system where a character's face is blended from two parent models. There are 46 possible mothers and fathers, each with distinct facial structures. The SetPedHeadBlendData native controls this blending, accepting shape and skin mix values between 0.0 and 1.0 that determine how much the character resembles each parent. Beyond heritage, the game offers 20 overlay categories through SetPedHeadOverlay covering features like blemishes, facial hair, eyebrows, aging, makeup, and skin damage. Each overlay has its own opacity value and can be colored independently using SetPedHeadOverlayColor. Hair is handled separately through SetPedComponentVariation for the model and SetPedHairColor for color and highlights. Eye color uses SetPedEyeColor with values ranging from 0 to 31. Understanding these natives is essential because your NUI interface needs to map user-friendly sliders and selectors to these specific function calls.

-- client/appearance.lua
function ApplyAppearance(ped, data)
    -- Heritage blend (mother, father, shape mix, skin mix)
    SetPedHeadBlendData(ped,
        data.mother, data.father, 0,
        data.mother, data.father, 0,
        data.shapeMix, data.skinMix, 0.0,
        false
    )

    -- Face features (-1.0 to 1.0 for each of 20 features)
    for i = 0, 19 do
        SetPedFaceFeature(ped, i, data.features[i] or 0.0)
    end

    -- Overlays (blemishes, beard, eyebrows, aging, makeup, etc.)
    for i = 0, 12 do
        local overlay = data.overlays[i] or {style = 0, opacity = 0.0}
        SetPedHeadOverlay(ped, i, overlay.style, overlay.opacity)
        if overlay.color then
            SetPedHeadOverlayColor(ped, i, overlay.colorType or 1,
                overlay.color, overlay.secondColor or overlay.color)
        end
    end

    -- Hair
    SetPedComponentVariation(ped, 2, data.hair or 0, 0, 2)
    SetPedHairColor(ped, data.hairColor or 0, data.hairHighlight or 0)

    -- Eye color
    SetPedEyeColor(ped, data.eyeColor or 0)
end

Building the NUI Interface

The NUI (Natural User Interface) is the HTML, CSS, and JavaScript overlay that players interact with while creating their character. A well-designed character creator divides the process into logical steps: biographical information first, then heritage selection, facial features, overlays like facial hair and makeup, hair styling, and finally clothing. Each step should show real-time preview updates on the character model so players can see exactly how their choices look. Use a tabbed or wizard-style layout so players aren't overwhelmed by dozens of options at once. Range sliders work well for continuous values like shape mix and facial features, while grid selectors with thumbnail previews work better for discrete options like hairstyles and clothing items. Send each change to the client script via fetch or the NUI message system so the ped updates instantly.

<!-- nui/index.html (simplified structure) -->
<div id="creator" class="creator-wrapper">
  <div class="steps-nav">
    <button data-step="bio" class="active">Identity</button>
    <button data-step="heritage">Heritage</button>
    <button data-step="features">Features</button>
    <button data-step="overlays">Appearance</button>
    <button data-step="hair">Hair</button>
    <button data-step="clothing">Clothing</button>
  </div>

  <div class="step-content" id="step-heritage">
    <label>Mother</label>
    <input type="range" id="mother" min="0" max="45" value="0">
    <label>Father</label>
    <input type="range" id="father" min="0" max="45" value="0">
    <label>Resemblance (Shape Mix)</label>
    <input type="range" id="shapeMix" min="0" max="100" value="50">
    <label>Skin Tone (Skin Mix)</label>
    <input type="range" id="skinMix" min="0" max="100" value="50">
  </div>
</div>

<script>
document.querySelectorAll('input[type="range"]').forEach(slider => {
  slider.addEventListener('input', () => {
    fetch('https://myresource/updateAppearance', {
      method: 'POST',
      body: JSON.stringify({
        type: slider.id,
        value: parseFloat(slider.value)
      })
    });
  });
});
</script>

Camera System for Character Preview

A dedicated camera system is crucial for a polished character creation experience. Players need to see close-up views of facial features when adjusting heritage blends and overlays, medium shots for hair and upper body clothing, and full-body views for pants and shoes. Implement a camera controller that smoothly interpolates between predefined positions based on which creation step is active. Use CreateCam with the DEFAULT_SCRIPTED_CAMERA type and RenderScriptCams to take control from the game camera. For rotation, allow players to drag the mouse to orbit around their character by adjusting the camera heading while keeping it pointed at the ped. A subtle idle animation on the ped, like the WORLD_HUMAN_STAND_IMPATIENT scenario, keeps the character looking natural during the creation process rather than standing in a rigid T-pose.

-- client/camera.lua
local creatorCam = nil
local camAngle = 0.0

local CamPositions = {
    bio      = {offset = vector3(0.0, 0.9, 0.65), fov = 35.0},
    heritage = {offset = vector3(0.0, 0.7, 0.65), fov = 30.0},
    features = {offset = vector3(0.0, 0.5, 0.68), fov = 22.0},
    overlays = {offset = vector3(0.0, 0.5, 0.68), fov = 22.0},
    hair     = {offset = vector3(0.0, 0.6, 0.72), fov = 25.0},
    clothing = {offset = vector3(0.0, 2.2, 0.30), fov = 45.0},
}

function SetupCreatorCam(ped, step)
    local pos = CamPositions[step] or CamPositions.bio
    local pedCoords = GetEntityCoords(ped)
    local target = pedCoords + vector3(0.0, 0.0, pos.offset.z)

    if not creatorCam then
        creatorCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
        RenderScriptCams(true, true, 500, true, false)
    end

    local rad = math.rad(camAngle)
    local camPos = pedCoords + vector3(
        math.sin(rad) * pos.offset.y,
        math.cos(rad) * pos.offset.y,
        pos.offset.z
    )

    SetCamCoord(creatorCam, camPos.x, camPos.y, camPos.z)
    PointCamAtCoord(creatorCam, target.x, target.y, target.z)
    SetCamFov(creatorCam, pos.fov)
    SetCamActive(creatorCam, true)
end

Saving and Loading Character Data

All character appearance data needs to persist in a database so it survives server restarts and can be loaded every time the player spawns. Structure your database table to store the complete appearance as a JSON blob alongside biographical fields like first name, last name, date of birth, gender, and nationality. When the player finishes creation and confirms their character, the client sends the full data object to the server, which validates the input ranges and inserts it into the database. On subsequent logins, the server fetches the character data during the spawn flow and sends it back to the client, which calls the same ApplyAppearance function used during creation. For multicharacter servers, store a character slot identifier so each player can have multiple characters with independent appearances.

-- server/database.lua
-- Using oxmysql for database operations
function SaveCharacter(playerId, charData)
    local identifier = GetPlayerIdentifier(playerId, 0)
    local appearance = json.encode(charData.appearance)

    MySQL.insert([[
        INSERT INTO characters
            (identifier, slot, firstname, lastname,
             dateofbirth, gender, nationality, appearance)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    ]], {
        identifier,
        charData.slot or 1,
        charData.firstname,
        charData.lastname,
        charData.dob,
        charData.gender,
        charData.nationality,
        appearance
    })
end

function LoadCharacter(playerId, slot)
    local identifier = GetPlayerIdentifier(playerId, 0)
    local result = MySQL.single.await([[
        SELECT * FROM characters
        WHERE identifier = ? AND slot = ?
    ]], {identifier, slot})

    if result then
        result.appearance = json.decode(result.appearance)
    end
    return result
end

-- Spawn handler
RegisterNetEvent('character:loaded', function(slot)
    local src = source
    local char = LoadCharacter(src, slot)
    if char then
        TriggerClientEvent('character:applyAppearance', src, char.appearance)
    end
end)

Clothing Integration and Component Variations

Clothing in GTA V is managed through component variations and prop variations. Components cover body parts like torso, legs, feet, accessories, and undershirts, while props handle attachable items like hats, glasses, earrings, and watches. The SetPedComponentVariation native takes a component ID, drawable ID, texture ID, and palette ID. The challenge is that available drawables differ between male and female ped models, and some drawable and texture combinations are invalid. Your NUI needs to query the maximum number of drawables for each component using GetNumberOfPedDrawableVariations and the maximum textures for each drawable using GetNumberOfPedTextureVariations. Build your clothing selector to dynamically populate options based on the selected gender so players only see valid combinations. Store clothing data alongside appearance data and apply it during the spawn flow using the same component variation natives.

Validation and Anti-Abuse Measures

Never trust data coming from the client without validation. Players with modified clients can send appearance values outside normal ranges, potentially crashing other clients or exploiting visual glitches. Validate every field on the server side before saving to the database. Heritage indices must be between 0 and 45, mix values between 0.0 and 1.0, face feature values between -1.0 and 1.0, and overlay indices within the valid range for each overlay type. Reject or clamp any out-of-range values. Additionally, implement rate limiting on appearance update events during creation to prevent malicious clients from flooding the server with rapid-fire appearance changes. A cooldown of 100 milliseconds between updates is imperceptible to legitimate players but stops automated spam. Finally, lock the character creation flow behind proper session management so players cannot trigger creation events outside the intended flow.

Share this article

Ready to upgrade your server?

Check out our premium FiveM scripts in the Agency Scripts store or join our Discord community for support and updates.