DrawMarker, help text and lib.showTextUI without lag in FiveM

Draw markers and show 'Press E' prompts in FiveM without a heavy loop: DrawMarker only when near, help text natives, lib.showTextUI and ox_lib points for zones.

You draw a marker, walk up to it and it flickers, or it never shows, or your script sits at 0.30 ms in resmon doing nothing. Markers and "Press E" prompts look simple, but they are the classic source of heavy loops. This article shows how to draw a marker correctly, how to show a prompt, and how to stop paying for it when the player is far away.

DrawMarker has to run every frame

A marker is drawn for one frame each time you call DrawMarker. To keep it on screen you call it again every frame, in a loop with Wait(0):

lua
local coords = vec3(441.2, -981.9, 30.7)

CreateThread(function()
    while true do
        DrawMarker(
            1,                                  -- type: cylinder
            coords.x, coords.y, coords.z - 1.0, -- position (on the ground)
            0.0, 0.0, 0.0,                      -- direction
            0.0, 0.0, 0.0,                      -- rotation
            1.5, 1.5, 0.5,                      -- scale
            50, 150, 255, 120,                  -- red, green, blue, alpha
            false, false, 2, false, nil, nil, false
        )
        Wait(0)
    end
end)

That works, but it runs every frame for the whole session, even when the player is across the map. Fix that next.

Only draw when the player is near

Measure the distance and sleep longer when the player is far. Only the near state needs Wait(0):

lua
local coords = vec3(441.2, -981.9, 30.7)

CreateThread(function()
    while true do
        local sleep = 1000
        local pedCoords = GetEntityCoords(PlayerPedId())
        local distance = #(pedCoords - coords)

        if distance < 15.0 then
            sleep = 0
            DrawMarker(1, coords.x, coords.y, coords.z - 1.0,
                0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.5, 1.5, 0.5,
                50, 150, 255, 120, false, false, 2, false, nil, nil, false)

            if distance < 1.5 then
                -- show the prompt here (next section)
            end
        end

        Wait(sleep)
    end
end)

Far away, the loop wakes once a second and does one subtraction. Near the marker it runs per frame, only while it needs to. Why this matters, and how to read it in resmon, is in resmon high ms: how to optimize.

Tip: #(a - b) is the length of a vector difference, the distance. It needs vec3 values, which GetEntityCoords already returns.

Marker types

The first argument is the marker type, a number. A few that servers use a lot:

Type Looks like
1 A cylinder
2 A chevron arrow (rotate it 180 degrees on X to point down)
23 A flat ring on the ground
25 A flat ring on the ground, thinner

The complete list of types is on the FiveM documentation site, under game references, markers. The scale arguments change the size, and the last colour value is the alpha (transparency). Place a ground marker at z - 1.0 so it sits on the floor, not floating at the player's waist.

Help text: the built-in prompt

The game has a help message at the top left of the screen. You define a label, then display it every frame while the player is in range:

lua
AddTextEntry('MY_SCRIPT_HELP', 'Press ~INPUT_CONTEXT~ to open the garage')

local function showHelp()
    BeginTextCommandDisplayHelp('MY_SCRIPT_HELP')
    EndTextCommandDisplayHelp(0, false, true, -1)
end

~INPUT_CONTEXT~ is replaced by the key the player uses for the context action, which is E by default. Call showHelp() inside the distance < 1.5 branch above, then check the key:

lua
if distance < 1.5 then
    showHelp()
    if IsControlJustReleased(0, 38) then
        print('opened')
    end
end

Register the label once at the top of the file, not inside the loop.

lib.showTextUI and lib.hideTextUI

With ox_lib you can show a styled prompt, and you call it once when the player enters, not every frame:

lua
lib.showTextUI('[E] Open the garage', {
    position = 'right-center',
    icon = 'warehouse',
})

-- when the player leaves:
lib.hideTextUI()

lib.isTextUIOpen() tells you if one is already on screen. It needs shared_script '@ox_lib/init.lua' in your manifest (see the ox_lib menu guide). The text is not bound to a key: you still check the key yourself, or use a keybind.

ox_lib points: no distance loop at all

Writing the distance loop by hand for each zone adds up. lib.points does it for you: ox_lib checks the player's distance to all points together, and only runs your code near them:

lua
local point = lib.points.new({
    coords = vec3(441.2, -981.9, 30.7),
    distance = 15.0,
})

function point:onEnter()
    lib.showTextUI('[E] Open the garage')
end

function point:onExit()
    lib.hideTextUI()
end

function point:nearby()
    DrawMarker(1, self.coords.x, self.coords.y, self.coords.z - 1.0,
        0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.5, 1.5, 0.5,
        50, 150, 255, 120, false, false, 2, false, nil, nil, false)

    if self.currentDistance < 1.5 and IsControlJustReleased(0, 38) then
        print('opened')
    end
end

How it works:

  • onEnter runs once when the player comes within distance.
  • onExit runs once when they leave.
  • nearby runs every frame while they are inside distance, and not at all otherwise. That is where DrawMarker goes.
  • self.currentDistance is the current distance to the point.

You can create hundreds of points and only the ones the player is near cost anything. Remove a point you no longer need with point:remove().

Which to use

  • One or two zones: the distance loop with a sleep is fine.
  • Many zones, or a script that creates zones from config: use lib.points.
  • Interactions on objects and peds: a target is better than a marker. See ox_target: how to add targets.

Checklist

Symptom Fix
Marker flickers or shows for one frame Call DrawMarker every frame in a Wait(0) loop
Script costs resmon time when idle Sleep 500 to 1000 ms when far away, Wait(0) only when near
Marker floats in the air Draw it at z - 1.0
Prompt text never shows Define it with AddTextEntry, then display it every frame
lib.showTextUI stays on screen Call lib.hideTextUI() on exit
Many zones are slow Use lib.points.new with onEnter, onExit and nearby

Quick answers

Why does my marker disappear after one frame?

DrawMarker has to be called every frame, in a loop with Wait(0). Call it once and it shows for a single frame.

Why does my script use so much resmon time?

A Wait(0) loop that measures distance to every marker runs each frame all the time. Sleep longer when the player is far away, or use ox_lib points so the loop only runs near the zone.

Which is better, help text or lib.showTextUI?

Help text is built into the game and needs no resource. lib.showTextUI is a styled prompt from ox_lib and is easy to show and hide from code. Pick one style and keep it for your whole server.

Scripts that skip this problem

Advanced BoostingTablet-driven vehicle boosting: contracts from class D to S+, crews and a live queue.View script →Shop CreatorBuild a shop in under a minute — owners, employees, vaults and robberies included.View script →Quest CreatorA visual editor for quests and NPC dialogues, built node by node in game.View script →

Keep reading