PiTyUs.Hire me

Engineering

Optimising FiveM Lua scripts

Make FiveM Lua fast: dynamic Wait in loops, event-driven code instead of polling, vector distance with #(a - b), caching natives and values (ox_lib cache), backtick hashes, avoiding table and string garbage, batching database work, and measuring with resmon and the profiler.

Updated 11 min readBy PiTyUs · FiveM developer

Overview

Most slow FiveM scripts are not slow because Lua is slow. They are slow because they do work every frame that only needed doing once a second — or never. A handful of patterns fixes the majority of resmon numbers you will see.

Dynamic Wait

client.lua — before and afterlua
-- before: 0.10+ ms, runs every frame everywhere
CreateThread(function()
    while true do
        Wait(0)
        if #(GetEntityCoords(PlayerPedId()) - SHOP) < 2.0 then
            DrawText3D(SHOP, '[E] Shop')
        end
    end
end)

-- after: ~0.00 ms away from the shop
CreateThread(function()
    while true do
        local sleep = 1000
        local dist = #(GetEntityCoords(cache.ped) - SHOP)
        if dist < 20.0 then sleep = 250 end
        if dist < 2.0 then
            sleep = 0
            DrawText3D(SHOP, '[E] Shop')
        end
        Wait(sleep)
    end
end)

Better still, use ox_lib points or zones, which do this bookkeeping for you — see zones. Thread basics: threads and Wait.

Events instead of polling

  • React to gameEventTriggered, framework job updates or state bag changes instead of checking every second.
  • Push data from the server when it changes instead of asking for it in a loop.
  • Use state bag change handlers for synced values — see state bags.

Cheap habits in hot code

SlowerFaster
GetDistanceBetweenCoords(...)#(a - b) with vectors
GetHashKey('prop') in a loop`prop` (compile-time)
PlayerPedId() several times per tickOnce per tick, or cache.ped
Building tables every frameReuse a table or build once
String concatenation in loopstable.concat or build once
One DB write per changeBatch writes, save on interval/logout

Server-side performance

  • Loops over all players every tick → every few seconds, spread across ticks.
  • Await database queries without blocking other work; add indexes — see database design.
  • Avoid huge JSON encodes on every save.
  • Hitches in the console point to long ticks — see profiler.

Measure

Open resmon 1 on the client and watch your resource while idle and while active. Aim for 0.00–0.02 ms idle. For server-side and deeper analysis, record with the profiler. Details: resmon explained.

Frequently asked questions

How do I get my FiveM script to 0.00 ms?

Sleep long when nothing is happening, only run every frame when drawing or reading input nearby, and use events instead of polling.

Is Wait(0) bad?

Only when it runs all the time. Use it when you must act every frame, and sleep otherwise.

What is the fastest distance check?

#(vectorA - vectorB) in Lua, without a native call.

How do I find what is slow?

resmon for per-resource time, then the profiler to see which functions take the time.

Need this built, not just explained?

Ten years of FiveM work, from Lua to NUI

Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.

Related guides