Scriptingintermediate13 min readupdated 19 Sept 2026

Showing weapons on a player's back in FiveM

The short answer

Get the weapon's world model with GetWeapontypeModel(hash), create it with CreateObject and attach it to the spine bone (SKEL_Spine3, bone id 24818, resolved with GetPedBoneIndex) using AttachEntityToEntity. Hide the prop while that weapon is in the player's hands. On ox_inventory the ped only holds the equipped weapon, so ask the inventory (GetItemCount) what the player carries. The cleanest sync is to publish the list in a player state bag and let every client draw local props for nearby players.

A rifle that simply vanishes when you holster it is one of the small things that makes GTA feel like GTA rather than a roleplay world. Slinging long guns on the player's back tells everyone nearby what a person is carrying before a word is said, and it is only a prop, a bone and an offset. The hard parts are knowing which weapons the player really owns on an inventory server, and making sure other players see the same thing you do.

Showing weapons on a player's back in FiveM — illustrated with a FiveM-compatible minimap render

#How a back-weapon script works

A back weapon is not a weapon at all. It is an ordinary object using the same model the game draws when a gun lies on the ground, glued to a bone on the ped's skeleton. The game has no idea it represents a rifle, which is exactly what you want: it cannot be fired, dropped or picked up, and it costs roughly what any small prop costs.

Every script that does this, from the old weapons-on-back releases to the ones bundled with modern frameworks, does the same four things:

  1. Work out which long weapons the player is carrying.
  2. For each one that is not currently in their hands, load the weapon's world model and create a prop.
  3. Attach the prop to a spine bone with a position and rotation offset so it sits across the back.
  4. Delete the prop the moment the weapon is drawn, and recreate it when it is put away.
JobNative
Model for a weaponGetWeapontypeModel(weaponHash)
Load the modelRequestModel / HasModelLoaded
Create the propCreateObject(model, x, y, z, isNetwork, netMissionEntity, doorFlag)
Find the boneGetPedBoneIndex(ped, 24818)
Attach itAttachEntityToEntity(...)
Weapon in handGetSelectedPedWeapon(ped)

The natives involved

#Choosing which weapons go on the back

Only long guns belong on the back. Pistols and tasers sit in a holster, which is a different job handled by a draw animation (see the holster guide). Melee weapons are a judgement call: a bat or a katana on the back reads well, a knife does not.

Keep the list in a config array rather than a keyed table. An array keeps its order, and that order becomes your priority when a player carries more long guns than you are willing to render.

config.lua
Config = {}

Config.Max = 2          -- props per player
Config.Range = 60.0     -- draw distance for other players
Config.Bone = 24818     -- SKEL_Spine3

Config.Weapons = {
    { name = 'WEAPON_CARBINERIFLE',   pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
    { name = 'WEAPON_ASSAULTRIFLE',   pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
    { name = 'WEAPON_SPECIALCARBINE', pos = vec3(0.075, -0.15, -0.02), rot = vec3(0.0, 165.0, 0.0) },
    { name = 'WEAPON_PUMPSHOTGUN',    pos = vec3(0.075, -0.15,  0.10), rot = vec3(0.0, 195.0, 0.0) },
    { name = 'WEAPON_SMG',            pos = vec3(0.075, -0.15,  0.10), rot = vec3(0.0, 195.0, 0.0) },
    { name = 'WEAPON_BAT',            pos = vec3(0.10,  -0.16,  0.00), rot = vec3(0.0, 225.0, 0.0) },
}

The numbers are a starting point, not gospel. Every model has its origin in a different place, so a rifle whose pivot sits at the grip needs a different offset from one whose pivot sits at the receiver. Tuning is covered further down.

#Creating the prop from the weapon's model

GetWeapontypeModel returns the model hash for any weapon the game knows about, including add-on weapons, because it reads the Model field from weapons.meta. That is why this approach works for a custom rifle without extra configuration, as long as the add-on's model is streamed and registered in weaponarchetypes.meta. The vanilla model names are listed in the weapon model names guide.

client/props.lua
local function loadModel(model)
    if model == 0 or not IsModelInCdimage(model) then return false end
    RequestModel(model)
    local deadline = GetGameTimer() + 5000
    while not HasModelLoaded(model) do
        if GetGameTimer() > deadline then return false end
        Wait(0)
    end
    return true
end

function CreateBackProp(ped, entry)
    local model = GetWeapontypeModel(joaat(entry.name))
    if not loadModel(model) then return nil end

    local c = GetEntityCoords(ped)
    local obj = CreateObject(model, c.x, c.y, c.z - 10.0, false, false, false)
    SetModelAsNoLongerNeeded(model)
    SetEntityCollision(obj, false, false)

    AttachEntityToEntity(obj, ped, GetPedBoneIndex(ped, Config.Bone),
        entry.pos.x, entry.pos.y, entry.pos.z,
        entry.rot.x, entry.rot.y, entry.rot.z,
        true, true, false, true, 2, true)

    return obj
end

The prop is created below the ped and attached immediately, so nobody sees it pop into existence at the player's feet. Collision is switched off because a rifle with collision catches doorframes and shoves the player sideways in tight corridors.

#AttachEntityToEntity, parameter by parameter

The attach native has fifteen parameters and most copy-pasted scripts get at least one of them wrong. In order:

#ParameterValue used hereWhy
1entity1the propThe thing being attached
2entity2the pedWhat it is attached to
3boneIndexGetPedBoneIndex(ped, 24818)Bone index, not bone id
4-6xPos, yPos, zPosconfig offsetPosition relative to the bone
7-9xRot, yRot, zRotconfig rotationRotation relative to the bone
10p9trueCommonly passed as true
11useSoftPinningtrueLets the attachment detach cleanly under force
12collisionfalseNo collision between prop and world
13isPedtrueThe prop is being attached to a ped
14rotationOrder2The order rotations are applied in
15syncRottrueKeep the rotation locked to the bone

AttachEntityToEntity parameters

The classic mistake is passing 24818 straight in as parameter three. That number is the bone id (SKEL_Spine3, 0x60F2); the native wants the index of that bone inside this ped's skeleton, which is what GetPedBoneIndex returns. Pass the raw id and the rifle ends up floating somewhere near the pelvis or the feet, and it will be in a different wrong place on a different ped model.

#Knowing what the player actually carries

This is where most back-weapon scripts break on modern servers. HasPedGotWeapon asks the ped, and on an inventory server the ped does not hold your whole arsenal.

  • ox_inventory gives the ped only the weapon that is currently equipped. Everything else is an item in a slot, so HasPedGotWeapon returns false for the rifle in your bag. Ask the inventory with the client export exports.ox_inventory:GetItemCount('WEAPON_CARBINERIFLE').
  • qb-inventory works the same way: weapons are items and the ped receives one when it is used. Read QBCore.Functions.GetPlayerData().items on the client and look for weapon items.
  • ESX with the built-in loadout really does give the ped every weapon, so HasPedGotWeapon(ped, hash, false) is correct there.
client/owned.lua
local useOx = GetResourceState('ox_inventory') == 'started'

local function carries(ped, name)
    if useOx then
        return exports.ox_inventory:GetItemCount(name) > 0
    end
    return HasPedGotWeapon(ped, joaat(name), false)
end

function GetBackList(ped)
    local held = GetSelectedPedWeapon(ped)
    local list = {}
    for _, entry in ipairs(Config.Weapons) do
        local hash = joaat(entry.name)
        if hash ~= held and carries(ped, entry.name) then
            list[#list + 1] = entry.name
            if #list >= Config.Max then break end
        end
    end
    return list
end

Excluding the weapon in hand is what makes the rifle "move" from the back to the hands when it is drawn. Nothing moves, of course: one prop is deleted and the real weapon appears.

#Making other players see it

There are two ways to make back weapons visible to everyone, and the difference matters more than any offset.

Networked props. Each client creates its own props with isNetwork = true, so the objects exist on the server and replicate. It works, but every player now owns several networked entities, they are left behind if a client crashes, and with sv_entityLockdown set to strict the client is not allowed to create them at all.

State bag plus local props. Each client publishes the list of weapons it carries on its own player state bag. Every client reads that list for players within range and creates non-networked props locally. Nothing is created on the server, nothing leaks when someone drops, and the whole thing costs one small replicated value per player.

client/sync.lua
local last = ''

CreateThread(function()
    while true do
        local list = GetBackList(PlayerPedId())
        local key = table.concat(list, ',')
        if key ~= last then
            last = key
            LocalPlayer.state:set('backWeapons', key, true)
        end
        Wait(750)
    end
end)
client/render.lua
local rendered = {} -- [serverId] = { key, ped, objs }

local function clear(id)
    local r = rendered[id]
    if not r then return end
    for _, obj in ipairs(r.objs) do
        if DoesEntityExist(obj) then DeleteEntity(obj) end
    end
    rendered[id] = nil
end

local function entryFor(name)
    for _, entry in ipairs(Config.Weapons) do
        if entry.name == name then return entry end
    end
end

CreateThread(function()
    while true do
        local myPos = GetEntityCoords(PlayerPedId())
        local seen = {}

        for _, player in ipairs(GetActivePlayers()) do
            local ped = GetPlayerPed(player)
            local id = GetPlayerServerId(player)
            local key = Player(id).state.backWeapons or ''

            if IsPedInAnyVehicle(ped, false) or IsEntityDead(ped)
                or #(GetEntityCoords(ped) - myPos) > Config.Range then
                key = ''
            end

            seen[id] = true
            local r = rendered[id]
            if not r or r.key ~= key or r.ped ~= ped then
                clear(id)
                local objs = {}
                for name in key:gmatch('[^,]+') do
                    local entry = entryFor(name)
                    local obj = entry and CreateBackProp(ped, entry)
                    if obj then objs[#objs + 1] = obj end
                end
                rendered[id] = { key = key, ped = ped, objs = objs }
            end
        end

        for id in pairs(rendered) do
            if not seen[id] then clear(id) end
        end
        Wait(500)
    end
end)

AddEventHandler('onResourceStop', function(res)
    if res ~= GetCurrentResourceName() then return end
    for id in pairs(rendered) do clear(id) end
end)

GetActivePlayers includes the local player, so the same loop draws your own back weapons. Comparing the stored ped handle catches respawns and model changes, which create a new ped and would otherwise leave the old props attached to nothing.

#Performance and edge cases

  • Cap the count. Two props per player is plenty. Twenty players with four rifles each is eighty attached objects for no gameplay gain.
  • Hide in vehicles. Seats clip through rifles on almost every car. The render loop above clears the props whenever the ped is in a vehicle.
  • Bags and vests. A large backpack (component 5) or a bulky vest (component 9) pushes the model out of alignment. Some servers keep a second offset per weapon for players wearing a bag; check GetPedDrawableVariation(ped, 5) and swap offsets.
  • Female peds. mp_f_freemode_01 has a slightly different spine. If rifles hover off female characters, add a small per-model correction.
  • Add-on weapons. A heavy weapon pack means heavy props. Oversized textures on weapons show up here too, because every back weapon streams the full model. Weapon pack performance covers budgets.

#Troubleshooting

SymptomCauseFix
Rifle floats at the hips or feetRaw bone id passed as the indexWrap it in GetPedBoneIndex(ped, 24818)
Prop visible only to youLocal prop and no syncPublish a state bag and render for every player
Nothing appears on ox_inventoryHasPedGotWeapon usedUse exports.ox_inventory:GetItemCount
Add-on weapon never showsModel not streamed or not in archetypesCheck IsModelInCdimage(model); see the streaming guides
Rifle stays on the back after drawingList not refreshedExclude GetSelectedPedWeapon and poll
Props left in the world after a crashNetworked props owned by the clientSwitch to the state bag approach

Common problems

If an add-on weapon shows in the hand but not on the back, the model hash is probably fine and the offset is the problem: a model whose origin sits far from the grip can end up a metre behind the player. Set the offset to zero and work outwards.

Share this guideXRedditFacebook

Questions

How do I put weapons on a player's back in FiveM?
Create the weapon's world model with CreateObject(GetWeapontypeModel(hash), ...) and attach it with AttachEntityToEntity to the bone index returned by GetPedBoneIndex(ped, 24818), then delete it whenever that weapon is in the player's hands.
Why do weapons on back not show with ox_inventory?
ox_inventory only gives the ped the equipped weapon, so HasPedGotWeapon is false for everything else. Check the inventory with exports.ox_inventory:GetItemCount(name) instead.
Which bone should a back weapon attach to?
SKEL_Spine3 (bone id 24818, 0x60F2) is the usual choice. Always convert it with GetPedBoneIndex before passing it to the attach native.
Do other players see my back weapons?
Only if you sync them. Publish the carried weapons in a player state bag and let every client create local props for nearby players, or create networked props if your entity lockdown allows it.
Does this work for add-on weapons?
Yes. GetWeapontypeModel reads the model from the add-on's weapons.meta, so any add-on that streams correctly can be shown on the back with its own offset.

Ready to pick a map?

Twelve themes on three base map styles, $8 each, instant download.

Relevant to what you just read

The tool for this

Twenty theme pairs for ox_lib and ox_inventory From $15, built in the browser, exported as a drop-in resource.

Look it up while you work

The reference tables this guide leans on, free and without an account.