Tutorial 2026-02-03

FiveM Emote & Animation Menu Development

OntelMonke

OntelMonke

Admin & Developer at Agency Scripts

Understanding GTA V Animation Dictionaries

Animations in GTA V are organized into animation dictionaries, which are collections of related animation clips. Each animation is identified by two strings: the dictionary name and the animation name within that dictionary. For example, the dictionary amb@world_human_hang_out_street@female_hold_arm@idle_a contains idle standing animations for female peds. Before you can play any animation, you must request the dictionary using RequestAnimDict and wait until it has finished loading with HasAnimDictLoaded. Failing to request the dictionary first is one of the most common mistakes new developers make, resulting in animations that silently fail to play. GTA V ships with thousands of animation dictionaries covering everything from combat stances to yoga poses, and discovering useful ones is half the challenge of building a comprehensive emote menu.

Playing and Managing Animations

The core function for playing animations is TaskPlayAnim, which accepts the player ped, dictionary, animation name, blend-in speed, blend-out speed, duration, flags, and playback rate. The animation flags control critical behavior: flag 1 loops the animation, flag 2 stops the animation on the last frame, flag 16 plays only the upper body allowing the player to walk, and flag 32 allows rotation during the animation. Combining flags with bitwise OR lets you create nuanced behaviors. A sitting emote needs flag 1 for looping plus flag 2 to hold the last frame if interrupted. A waving emote works best with flag 49 which combines upper body only with looping and rotation, letting the player wave while walking around.

-- Core animation helper functions
local function LoadAnimDict(dict)
    if HasAnimDictLoaded(dict) then return true end
    RequestAnimDict(dict)
    local timeout = GetGameTimer() + 5000
    while not HasAnimDictLoaded(dict) do
        Wait(10)
        if GetGameTimer() > timeout then
            return false
        end
    end
    return true
end

local function PlayEmote(dict, anim, flags, duration)
    local ped = PlayerPedId()
    if IsPedInAnyVehicle(ped, false) then return end
    if not LoadAnimDict(dict) then return end

    flags = flags or 1      -- default: loop
    duration = duration or -1 -- -1 = indefinite

    TaskPlayAnim(ped, dict, anim, 2.0, 2.0, duration, flags, 0, false, false, false)
end

local function StopCurrentEmote()
    local ped = PlayerPedId()
    ClearPedTasks(ped)
    -- Also remove any attached props
    ClearPedSecondaryTask(ped)
end

-- Export for other resources
exports('PlayEmote', PlayEmote)
exports('StopEmote', StopCurrentEmote)

Organizing Emotes into Categories

A well-organized emote menu groups animations into intuitive categories that players can quickly browse. Common categories include Dances, Greetings, Reactions, Poses, Sports, Scenarios (like sitting, leaning, or lying down), Prop Emotes (involving objects like cigarettes or phones), Walking Styles, and Facial Expressions. Each emote entry should store the dictionary, animation name, flags, whether it uses a prop, the prop model hash if applicable, the bone index for prop attachment, and the offset and rotation values for proper prop placement. Storing all of this in a structured configuration table makes it easy to add new emotes without modifying the playback logic.

-- Emote configuration (config.lua)
Config = {}

Config.Emotes = {
    dances = {
        {label = 'Dance 1',    dict = 'anim@amb@nightclub@dancers@club_dance_bar_1',    anim = 'high_center',   flags = 1},
        {label = 'Dance 2',    dict = 'anim@amb@nightclub@mini@dance@dance_solo@female@var_a@',  anim = 'high_center',   flags = 1},
        {label = 'Dance 3',    dict = 'anim@amb@nightclub@dancers@crowddance_facedj@',  anim = 'hi_dance_facedj_09_v2_female^1', flags = 1},
        {label = 'Flamenco',   dict = 'special_ped@jessie@idle_latina',                 anim = 'idle_latina',   flags = 1},
    },
    greetings = {
        {label = 'Wave',       dict = 'friends@frj@ig_1',    anim = 'wave_A',         flags = 49, duration = 3000},
        {label = 'Salute',     dict = 'anim@mp_player_intuppersalute',  anim = 'idle_a',  flags = 49, duration = 3000},
        {label = 'Handshake',  dict = 'mp_ped_interaction',  anim = 'handshake_guy_a', flags = 49, duration = 4000},
        {label = 'Fist Bump',  dict = 'mp_ped_interaction',  anim = 'fist_bump',      flags = 49, duration = 3000},
    },
    props = {
        {label = 'Cigarette',  dict = 'amb@world_human_smoking@male@male_a@idle_a', anim = 'idle_c', flags = 49,
            prop = 'prop_cs_ciggy_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
        {label = 'Coffee',     dict = 'amb@world_human_drinking@coffee@male@idle_a', anim = 'idle_c', flags = 49,
            prop = 'p_amb_coffeecup_01', bone = 28422, pos = {0.0, 0.0, 0.0}, rot = {0.0, 0.0, 0.0}},
        {label = 'Clipboard',  dict = 'amb@world_human_clipboard@male@idle_a', anim = 'idle_c', flags = 49,
            prop = 'p_amb_clipboard_01', bone = 36029, pos = {0.16, 0.08, 0.03}, rot = {-100.0, 0.0, 0.0}},
    },
    scenarios = {
        {label = 'Sit Chair',  scenario = 'PROP_HUMAN_SEAT_CHAIR', isScenario = true},
        {label = 'Lean Wall',  scenario = 'WORLD_HUMAN_LEANING',   isScenario = true},
        {label = 'Guard',      scenario = 'WORLD_HUMAN_GUARD_STAND', isScenario = true},
        {label = 'Jog',        scenario = 'WORLD_HUMAN_JOG_STANDING', isScenario = true},
    },
}

Prop Emotes and Object Attachment

Prop emotes involve spawning a game object and attaching it to a specific bone on the player's ped. GTA V has a skeletal bone system where each bone has a unique index. Common attachment bones include 57005 for the right hand, 18905 for the left hand, 28422 for the right finger area (ideal for cigarettes), and 31086 for the head. After spawning the prop with CreateObject, attach it using AttachEntityToEntity which requires the prop entity, the ped entity, the bone index, position offsets, rotation offsets, and several boolean flags for collision and physics behavior. Getting the position and rotation offsets right requires trial and error, and different ped models may need slight adjustments. Always clean up props when the emote is cancelled by tracking the spawned entity handle and deleting it.

-- Prop emote system
local currentProp = nil

local function PlayPropEmote(emoteData)
    local ped = PlayerPedId()
    if IsPedInAnyVehicle(ped, false) then return end

    -- Clean up any existing prop
    if currentProp and DoesEntityExist(currentProp) then
        DeleteEntity(currentProp)
        currentProp = nil
    end

    if not LoadAnimDict(emoteData.dict) then return end

    -- Spawn and attach prop
    local model = GetHashKey(emoteData.prop)
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(10) end

    local pos = GetEntityCoords(ped)
    currentProp = CreateObject(model, pos.x, pos.y, pos.z, true, true, false)
    SetModelAsNoLongerNeeded(model)

    local boneIndex = GetPedBoneIndex(ped, emoteData.bone)
    AttachEntityToEntity(
        currentProp, ped, boneIndex,
        emoteData.pos[1], emoteData.pos[2], emoteData.pos[3],
        emoteData.rot[1], emoteData.rot[2], emoteData.rot[3],
        true, true, false, true, 1, true
    )

    -- Play animation
    TaskPlayAnim(ped, emoteData.dict, emoteData.anim,
        2.0, 2.0, -1, emoteData.flags or 49, 0, false, false, false)
end

-- Cleanup on emote cancel
local function CleanupProps()
    if currentProp and DoesEntityExist(currentProp) then
        DeleteEntity(currentProp)
        currentProp = nil
    end
end

Walking Styles and Movement Clips

Walking styles change how the player character moves and are applied using SetPedMovementClipset. Unlike regular animations that override actions, movement clipsets persist across all locomotion states including walking, running, and sprinting. GTA V includes dozens of built-in movement clipsets such as MOVE_M@DRUNK@MODERATEDRUNK for a drunk stumble, MOVE_M@GANGSTER@NG for a gangster swagger, MOVE_M@CONFIDENT for a confident stride, and MOVE_F@HEELS@C for walking in high heels. Like animation dictionaries, clipsets must be requested and loaded before application. Provide a reset option so players can return to the default walk. Store the player's preferred walking style in their character data so it persists across sessions, reapplying it automatically when they log in.

-- Walking style system
local currentWalkStyle = nil

Config.WalkStyles = {
    {label = 'Default',     clipset = nil},
    {label = 'Drunk',       clipset = 'MOVE_M@DRUNK@MODERATEDRUNK'},
    {label = 'Gangster',    clipset = 'MOVE_M@GANGSTER@NG'},
    {label = 'Confident',   clipset = 'MOVE_M@CONFIDENT'},
    {label = 'Tough',       clipset = 'MOVE_M@TOUGH_GUY@'},
    {label = 'Femme',       clipset = 'MOVE_F@FEMME@'},
    {label = 'Heels',       clipset = 'MOVE_F@HEELS@C'},
    {label = 'Injured',     clipset = 'move_m@injured'},
    {label = 'Hobo',        clipset = 'MOVE_M@HOBO@A'},
    {label = 'Brave',       clipset = 'MOVE_M@BRAVE@A'},
}

local function SetWalkStyle(clipset)
    local ped = PlayerPedId()
    ResetPedMovementClipset(ped, 0.2)

    if not clipset then
        currentWalkStyle = nil
        return
    end

    RequestClipSet(clipset)
    local timeout = GetGameTimer() + 3000
    while not HasClipSetLoaded(clipset) do
        Wait(10)
        if GetGameTimer() > timeout then return end
    end

    SetPedMovementClipset(ped, clipset, 0.2)
    currentWalkStyle = clipset
end

exports('SetWalkStyle', SetWalkStyle)
exports('GetWalkStyle', function() return currentWalkStyle end)

Facial Expressions

Facial expressions add subtle emotional depth to roleplay interactions. GTA V supports facial animations through SetFacialIdleAnimOverride, which changes the ped's resting face expression. Available expressions include mood_normal_1, mood_happy_1, mood_angry_1, mood_injured_1, mood_stressed_1, mood_smug_1, and mood_sulk_1 among others. These expressions persist until cleared with ClearFacialIdleAnimOverride. Combining a facial expression with a body animation and a walking style gives players full control over how their character presents themselves. A character leaning against a wall with a smug expression and a confident walk tells a completely different story than one with an injured limp and a stressed face. These small details elevate roleplay from basic interactions to rich character portrayal.

-- Facial expression system
local currentExpression = nil

Config.Expressions = {
    {label = 'Normal',    expression = 'mood_Normal_1'},
    {label = 'Happy',     expression = 'mood_Happy_1'},
    {label = 'Angry',     expression = 'mood_Angry_1'},
    {label = 'Injured',   expression = 'mood_Injured_1'},
    {label = 'Stressed',  expression = 'mood_Stressed_1'},
    {label = 'Smug',      expression = 'mood_smug_1'},
    {label = 'Sulking',   expression = 'mood_sulk_1'},
    {label = 'Sleeping',  expression = 'pose_injured_1'},
    {label = 'Scared',    expression = 'mood_scared_1'},
}

local function SetExpression(expression)
    local ped = PlayerPedId()
    ClearFacialIdleAnimOverride(ped)
    if expression then
        SetFacialIdleAnimOverride(ped, expression, nil)
        currentExpression = expression
    else
        currentExpression = nil
    end
end

exports('SetExpression', SetExpression)

-- Shared emotes: sync with nearby players
RegisterNetEvent('emote:syncShared', function(senderId, dict, anim)
    local senderPed = GetPlayerPed(GetPlayerFromServerId(senderId))
    if not DoesEntityExist(senderPed) then return end
    local myPed = PlayerPedId()
    local dist = #(GetEntityCoords(myPed) - GetEntityCoords(senderPed))
    if dist > 3.0 then return end
    if not LoadAnimDict(dict) then return end
    TaskPlayAnim(myPed, dict, anim, 2.0, 2.0, -1, 1, 0, false, false, false)
end)

Building the Emote Menu Interface

The emote menu can be implemented as either a NUI-based radial menu or an in-game menu using a library like ox_lib or qb-menu. A radial menu feels natural for emotes because players can quickly navigate categories and select an emote without reading through long text lists. Bind the menu to a key like F4 or a configurable keybind, and provide a command fallback like /emote [name] for direct access. The menu should show the emote name, an optional preview thumbnail, and indicate which emotes use props. Include a favorites system that lets players pin their most-used emotes to a quick-access ring, saving them from browsing categories every time they want to perform a common action. Persist favorites in the player's KVP storage using SetResourceKvp so they survive server restarts without needing database writes. A well-built emote menu becomes one of the most-used features on any roleplay server, so investing time in the interface pays dividends in player satisfaction.

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.