Lua Optimization Tips for FiveM Scripts
Performance tips and best practices for writing efficient Lua scripts that won't lag your server.


Writing Performant FiveM Scripts
Lag kills roleplay servers. Here's how to write Lua that keeps your server running smooth.
The Golden Rules
- Never run expensive operations every frame
- Cache everything you can
- Use natives wisely
- Profile before you optimize
Thread Management
The biggest performance killer: threads with Wait(0) or no wait at all.
-- ❌ BAD: Runs every frame (1ms = 1000 calls per second)
CreateThread(function()
while true do
Wait(0)
-- This runs 1000 times per second!
local coords = GetEntityCoords(PlayerPedId())
end
end)
-- ✅ GOOD: Runs every second (1000ms)
CreateThread(function()
while true do
Wait(1000)
-- This runs once per second
local coords = GetEntityCoords(PlayerPedId())
end
end)
-- ✅ BETTER: Adaptive waiting
CreateThread(function()
while true do
local sleep = 1000
local coords = GetEntityCoords(PlayerPedId())
-- Only speed up when near something important
if IsNearImportantLocation(coords) then
sleep = 100
end
Wait(sleep)
end
end)
Caching Strategies
Cache values that don't change often:
-- ❌ BAD: Getting ped every time
CreateThread(function()
while true do
Wait(0)
local ped = PlayerPedId() -- Called 1000x/sec
local health = GetEntityHealth(ped)
end
end)
-- ✅ GOOD: Cache the ped reference
local cachedPed = PlayerPedId()
CreateThread(function()
while true do
Wait(5000)
cachedPed = PlayerPedId() -- Update every 5 seconds
end
end)
CreateThread(function()
while true do
Wait(100)
local health = GetEntityHealth(cachedPed)
end
end)
Table Operations
Lua tables are powerful but can be slow if misused:
-- ❌ BAD: Using table.insert in hot loops
local results = {}
for i = 1, 10000 do
table.insert(results, someValue)
end
-- ✅ GOOD: Direct index assignment
local results = {}
for i = 1, 10000 do
results[i] = someValue
end
-- ❌ BAD: Checking table length in loops
for i = 1, #largeTable do
-- #largeTable is calculated each iteration
end
-- ✅ GOOD: Cache the length
local len = #largeTable
for i = 1, len do
-- Much faster
end
Native Optimization
Some natives are expensive. Use them wisely:
-- Expensive natives (use sparingly):
-- GetClosestVehicle, GetClosestPed
-- GetGamePool
-- TaskWarpPedIntoVehicle (causes sync issues)
-- ❌ BAD: Finding closest vehicle every frame
CreateThread(function()
while true do
Wait(0)
local vehicle = GetClosestVehicle(coords, 50.0, 0, 71)
end
end)
-- ✅ GOOD: Use a zone-based approach
local nearbyVehicles = {}
CreateThread(function()
while true do
Wait(2000)
nearbyVehicles = GetNearbyVehicles() -- Custom efficient function
end
end)
Event Optimization
Events are great, but can be abused:
-- ❌ BAD: Triggering events too frequently
CreateThread(function()
while true do
Wait(0)
TriggerServerEvent('player:updatePosition', GetEntityCoords(ped))
end
end)
-- ✅ GOOD: Throttle event frequency
local lastUpdate = 0
CreateThread(function()
while true do
Wait(100)
local currentTime = GetGameTimer()
if currentTime - lastUpdate > 5000 then
TriggerServerEvent('player:updatePosition', GetEntityCoords(ped))
lastUpdate = currentTime
end
end
end)
Memory Management
Lua has garbage collection, but you can help it:
-- ❌ BAD: Creating new tables constantly
CreateThread(function()
while true do
Wait(100)
local data = {x = 0, y = 0, z = 0} -- New table every 100ms
end
end)
-- ✅ GOOD: Reuse tables
local data = {x = 0, y = 0, z = 0}
CreateThread(function()
while true do
Wait(100)
data.x, data.y, data.z = table.unpack(GetEntityCoords(ped))
end
end)
Profiling Your Code
Always measure before optimizing:
local function profile(name, fn)
local start = GetGameTimer()
fn()
print(name .. ' took ' .. (GetGameTimer() - start) .. 'ms')
end
profile('ExpensiveOperation', function()
-- Your code here
end)
Common Patterns
Distance Checks
-- ❌ BAD: Using native distance
local dist = #(coords1 - coords2)
-- ✅ GOOD: Squared distance (avoid sqrt)
local function distanceSquared(c1, c2)
local dx, dy, dz = c1.x - c2.x, c1.y - c2.y, c1.z - c2.z
return dx*dx + dy*dy + dz*dz
end
-- Then compare against squared threshold
if distanceSquared(coords1, coords2) < (5.0 * 5.0) then
-- Within 5 units
end
The Optimization Checklist
- [ ] No
Wait(0)without good reason - [ ] Cached player ped and coordinates
- [ ] Events are throttled appropriately
- [ ] Tables are reused where possible
- [ ] Expensive natives are minimized
- [ ] Distance checks use squared values
- [ ] Profiled and measured actual impact
Remember: premature optimization is the root of all evil. Measure first, then optimize the hot paths.

Passionate about building great digital experiences. When not coding, you can find me exploring new technologies and sharing knowledge with the community.

