Grand opening25% off everything with FAM-261023-261025-UL4B9this weekendTick it in your cart Halloween Edition36 maps, limited runlimited runShop the drop

FREE TOOL · PERFORMANCE

FiveM profiler analyzer

The profiler tells you the truth about which resource is eating your frame, and then hands it to you as a Chrome timeline that is pinned to one frozen DevTools build and shows thousands of unlabelled bars. Drop the capture here and get the answer instead: the resources ranked by their own time, the loops inside them, the worst frames as flame charts, and what each pattern usually means. Nothing is uploaded — the file is read in this tab.

Drop a profiler capture here

The file profiler saveJSON writes. It is read by this page and nothing else: there is no upload, no API call and no server involved — close the tab and it is gone.

HOW TO USE IT

From a stutter to the line that causes it

Six steps. The first one is the one people skip, and skipping it wastes the other five.

  1. Record while it is actually bad. The profiler records a fixed number of frames from the moment you ask, so start it when the server is full and the stutter is happening — not at four in the morning on an empty server. In the F8 console or the server console: profiler record 500. Five hundred frames is about eight seconds at 60 fps and is plenty.
  2. Save it as JSON, not as the default. profiler saveJSON mycapture.json. The similarly named profiler save writes a binary msgpack file that nothing will open, including this page — although it will recognise it and tell you so rather than failing silently. Server captures land beside run.bat; client captures land in the FiveM application folder.
  3. Drop it in and read the findings first. They are ordered by how much they matter, not by what the file happens to contain most of. Anything marked serious is either over the frame budget or a measurable per-frame cost; anything marked for information is there so you stop wondering about it.
  4. Sort the resource table by self time, not total. Self time is a resource's own code. Total includes everything it triggered elsewhere, so a resource that fires an event into five others looks enormous by total and may be doing almost nothing itself. The thing to rewrite is at the top of the self column.
  5. Open the loop table and look at the frames column. A loop present in 420 out of 421 frames is running on every frame. If its cost per frame barely moves, nothing in it is gated — that is the shape of a draw loop with no distance check, or a table rebuilt from scratch every frame. The file name and line range next to it is the function to open.
  6. Then prove it. Stop the resource you suspect, capture again, and compare the median tick. If the number drops by roughly what the table said it would, you have found it. If it does not, the cost moved and the resource was only the messenger — which is exactly the mistake a single capture makes easy.

BE PRECISE ABOUT IT

What is actually in a profiler capture, and what is not

Most of what people believe about these files is guessed from the DevTools view. Here is what the file holds, where that comes from, and what no amount of reading it will tell you.

Begin and end events, in microseconds

Every slice is two events — a begin and an end — carrying a timestamp in microseconds counted from the start of the recording, not from any clock you can compare against. There is no duration field anywhere in the file, so every length on this page is a subtraction and every self time is that minus the children.

One thread for all the scripts

A profiler record capture puts every slice on a single script thread under one root per frame, named Resource Manager Tick. A second thread carries only frame boundaries and screenshots. The per-resource threads you may have read about appear only in profiler resource mode.

Resource entries carry their cause

A resource slice is named for why it was entered and then the resource in brackets — tick (chat) for a normal frame, event:playerSpawned (chat) for an event delivery. That is how this page separates work a resource does on its own schedule from work something else asked it to do.

No native calls at all

The profiler does not instrument native invocation. Nothing in the file names DrawMarker, GetPlayerPed, SendNUIMessage or any other native, and no tool can recover them from it. Advice about those on this page is a pattern match on timing shape, and every finding that does it says so.

No line numbers as data

The line numbers you see are text baked into a Lua thread's name — the first and last line of the function, not the expensive line inside it. There is no structured source position, no call stack array and no sampled profile. For JavaScript and C# resources there is not even that.

No reliable memory figure

There is a counter event with a field called jsHeapSizeUsed, because the file is shaped to be loadable by the Chrome timeline. What FiveM puts in it is a change in memory use rather than a heap size, it can be negative, and it is sampled every sixteenth scope. This page reports it and marks it unreliable.

The documented profiler page covers the commands profiler record, profiler status, profiler saveJSON and profiler view, and says the result is loaded in the Chrome DevTools performance panel. It does not describe the file's structure at all: it does not contain the word traceEvents, any field name, or the commands profiler save, record start, record stop or resource. Everything on this page about the structure was read off the emitting source, Profiler.cpp, rather than guessed — but because it is undocumented it is not a contract, and Cfx.re can change it without telling anybody. The parser here is deliberately tolerant: it accepts a bare event array and complete events with a duration field as well, even though current FiveM writes neither, and the What the file holds tab shows you exactly what it found so you can see when the shape has moved.

THE PATTERNS

The five shapes that account for most of it

Each of these is something the analyzer looks for by name. What it can measure and what it is inferring are kept apart.

  1. A Wait(0) loop with a body. The thread appears in nearly every frame and costs real time in each one. Measured, not inferred: the capture holds the frame count and the per-frame cost. The fix is almost never to make the body faster — it is to run it less often. Two speeds in one thread is the usual answer: a slow outer loop that decides whether anything is nearby, and a Wait(0) inner loop that only spins while it is.
  2. A draw loop with no distance gate. Same shape as above, plus a cost that barely moves from the cheapest frame to the most expensive. Nothing in the loop returns early, so it pays for the marker whether you are standing on it or across the map. Inferred from the flatness, because the profiler cannot see the draw call. Get the player coordinates once, compare squared distances, and skip the body outside the radius.
  3. An un-cached lookup inside the loop. This one the file cannot see directly at all, and it is the most common single cause of the two above. PlayerPedId() or GetPlayerPed(-1) called every frame, often several times, when the value changes only when you respawn. Cache it outside the loop and refresh it on the spawn event.
  4. A NUI or callback storm. Reference calls — the game calling back into Lua through a stored function — happening several times per frame. Counted, not inferred. An interface almost never needs updating at frame rate: one message every 100 ms carrying the whole state beats ten a frame carrying one field each, and nobody can see the difference.
  5. A query blocking a server tick. A single slice holding the thread for more than ten milliseconds with nothing nested inside it. Measured. On a server this stops everybody at once, and it is nearly always a synchronous database call or an HTTP request waited on in the handler. The analyzer tells client and server captures apart before giving the advice, because the fix is different.

QUESTIONS

About profiler captures

How do I get the file this page reads?
In the client F8 console or the server console: profiler record 500 to record five hundred frames, wait for it to finish, then profiler saveJSON mycapture.json. On the server the file lands beside run.bat; on the client it lands in the FiveM application folder. Record while the problem is actually happening — a capture of a good minute tells you nothing about a bad one.
Why does my profiler save file not open here?
Because profiler save and profiler saveJSON write different formats. profiler save writes msgpack, a binary format, even if you name the file .json — that is the single most common reason a capture "will not open" anywhere. This page detects it by the first byte and tells you. Record again with saveJSON, or run profiler view mycapture on the binary one, which converts it.
Can it tell me which native is slow?
No, and neither can anything else reading this file: the profiler does not instrument native calls at all. What it records is resource entries, exits, Lua threads, event handlers and reference calls. So this page can tell you that a loop in cl_hud.lua between lines 14 and 61 costs 0.9 ms every single frame and never gets cheaper — which is enough to find the problem — but the DrawMarker or GetPlayerPed inside it is something you see when you open the file. We say which conclusions are measured and which are a pattern match, on every finding.
Why is there no duration field in the numbers?
Because there is none in the file. FiveM emits only begin and end events with a timestamp in microseconds, so every duration on this page is computed by matching ends to begins per thread, and self time is the duration minus the children. That is also why a capture that was stopped part-way through a frame leaves slices with no end: we close those at the last timestamp and label them, rather than reporting a made-up length.
Why does one of my resources show a total and nothing inside it?
Because the profiler only takes code apart for Lua. A resource written in JavaScript or C# is one opaque block by design, and so is a resource whose Lua is escrowed. You get what it costs and not where, which is still the number to send to whoever wrote it. The page lists these separately so you do not spend an hour wondering why the detail is missing.
Does my capture get uploaded?
No. The file is read by JavaScript in your own tab with the File API, parsed there and thrown away when you close the page. There is no API route behind this tool and no request leaves the browser with your data in it — which matters more than usual here, because a client capture embeds a screenshot of every frame it recorded.
The file is 200 MB. Is that normal?
For a client capture, yes. Every frame is stored with a base64 JPEG screenshot of itself, which is most of the size and affects none of the timing. The analyzer counts them and tells you how much of the file they are. If the tab struggles, record fewer frames: three hundred is plenty to find a per-frame loop.

FROM THE SHOP

What we build when it is not free

The free tools and the paid work come out of the same place. These are the lines closest to what this page is about.

Grand opening: 25% off every product with FAM-261023-261025-UL4B9, until 25 October. It is applied at checkout.

Loading screens

Loading screens for FiveM

The one screen every player sees on every connect. Drag widgets onto a canvas, drop in a video background, and point your server at a single URL we host.

Free50 live widgets · 30 fontsOpen the screen builder
The Downtown Dusk graphics design, shown at Discord banner size
Graphics packs

Graphics packs for FiveM

One design, cut to every slot your server shows up in: the server list strip, the Discord header, the stream art, each composed for its own crop.

From $12.00124 designs · 20 formatsBrowse the designs
The Onyx weapon wheel design in polished black glass, drawn over the game HUD
Weapon wheels

Weapon wheels for FiveM

A native hud.gfx replacement rather than an NUI overlay, so it costs no frame time. Colours, shape and motion are yours before you pay.

From $15.0050 wheel designsBrowse the wheels