# A real progress bar for your FiveM loading screen

> Build an accurate FiveM loading screen progress bar from the loading events: loadProgress and loadFraction, startInitFunctionOrder, initFunctionInvoking, performMapLoadFunction and onLogLine — with working JavaScript and a browser test harness.

Source: https://fam-tools.com/guides/fivem-loading-screen-progress-bar · FAM Tools · published 2026-09-11 · updated 2026-09-11 · about 12 min

**Short answer.** Listen for `message` events on `window`. `loadProgress` carries `loadFraction` (0–1) — the simplest source for a bar. For finer detail, `startInitFunctionOrder` gives a `count`, `initFunctionInvoking` gives the current `idx`, `startDataFileEntries` gives a `count` of map files and `performMapLoadFunction` fires per file; `onLogLine` gives a `message` to show under the bar. Keep the displayed value monotonic so it never goes backwards.

## Key points

- All loading events arrive as `window` `message` events; switch on `event.data.eventName`.
- `loadProgress` → `loadFraction` between 0 and 1 is the easiest bar source.
- Init phases come in types: `INIT_BEFORE_MAP_LOADED`, `INIT_AFTER_MAP_LOADED`, `INIT_SESSION`.
- `onLogLine` → `message` makes a good status line.
- Never let the bar go backwards: keep the highest value you have shown.

Players forgive a long load if they can see it moving. A frozen screen for ninety seconds feels broken; a bar that creeps forward with a line of text underneath feels like progress. FiveM tells the loading screen exactly what it is doing through a stream of events — this guide shows what each one means and how to turn them into a progress bar that never jumps backwards.

## The events FiveM sends

While the game loads, FiveM posts messages to the loading screen page. These are the ones documented by Cfx and used by the official example loading screen:

| eventName | Data you get | Meaning |
| --- | --- | --- |
| `loadProgress` | `loadFraction` (0–1) | Overall progress estimate |
| `startInitFunction` | `type` | An init phase begins |
| `startInitFunctionOrder` | `type`, `order`, `count` | A group of `count` init functions starts |
| `initFunctionInvoking` | `type`, `name`, `idx` | Init function number `idx` is running |
| `initFunctionInvoked` | `type`, `name`, `idx` | It finished |
| `endInitFunction` | `type` | The phase ended |
| `startDataFileEntries` | `count` | `count` map data files will load |
| `onDataFileEntry` | file details | A data file is being processed |
| `performMapLoadFunction` | `idx` | One map load step ran |
| `endDataFileEntries` | — | Map data loaded |
| `onLogLine` | `message` | A human-readable status line |

The init `type` is one of `INIT_BEFORE_MAP_LOADED`, `INIT_AFTER_MAP_LOADED` or `INIT_SESSION`, which lets you label the phase for the player.

## The simplest working bar

*index.html*

```html
<div class="bar"><div class="fill" id="fill"></div></div>
<p class="status" id="status">Connecting…</p>
```

*script.js*

```javascript
const fill = document.getElementById('fill');
const status = document.getElementById('status');
let shown = 0;

function setProgress(fraction) {
  shown = Math.max(shown, Math.min(1, fraction)); // never go backwards
  fill.style.width = (shown * 100).toFixed(1) + '%';
}

window.addEventListener('message', (e) => {
  const d = e.data;
  if (d.eventName === 'loadProgress') setProgress(d.loadFraction);
  if (d.eventName === 'onLogLine') status.textContent = d.message;
});
```

*style.css*

```css
.bar  { width: min(520px, 60vw); height: 6px; border-radius: 99px; background: rgba(255,255,255,.12); overflow: hidden; }
.fill { height: 100%; width: 0; background: #5b6af0; transition: width .4s ease; }
```

## A more detailed bar from the init phases

`loadFraction` can sit still for a while and then leap. Combining it with the init and map events gives smoother motion and better labels. The pattern below mirrors the official example loading screen: remember the current `count`, then fill proportionally as `idx` or map steps advance.

*script.js — phase-aware*

```javascript
// assumes <div id="phase"> (thin bar) and <p id="detail"> in the page
const phase = document.getElementById('phase');
const detail = document.getElementById('detail');

const LABEL = {
  INIT_BEFORE_MAP_LOADED: 'Preparing the game',
  INIT_AFTER_MAP_LOADED: 'Loading the city',
  INIT_SESSION: 'Joining the session',
};
let count = 0;
let mapSteps = 0;

const handlers = {
  loadProgress: (d) => setProgress(d.loadFraction),
  startInitFunctionOrder: (d) => {
    count = d.count;
    status.textContent = LABEL[d.type] || 'Loading';
  },
  initFunctionInvoking: (d) => {
    if (count) phase.style.width = ((d.idx / count) * 100) + '%';
  },
  startDataFileEntries: (d) => { count = d.count; mapSteps = 0; },
  performMapLoadFunction: () => {
    mapSteps += 1;
    if (count) phase.style.width = ((mapSteps / count) * 100) + '%';
  },
  onLogLine: (d) => { detail.textContent = d.message; },
};

window.addEventListener('message', (e) => {
  (handlers[e.data.eventName] || (() => {}))(e.data);
});
```

> **Two bars, not one** — A thin “phase” bar that resets for each step, under a main bar driven by `loadFraction`, gives constant visible motion without the main bar ever going backwards.

## Testing the bar in a browser

Outside the game nothing sends these events. Paste this into the DevTools console of your page to simulate a load:

*Fake a load in the browser console*

```javascript
let f = 0;
const t = setInterval(() => {
  f += Math.random() * 0.04;
  window.postMessage({ eventName: 'loadProgress', loadFraction: Math.min(f, 1) }, '*');
  window.postMessage({ eventName: 'onLogLine', message: 'Loading resource ' + Math.round(f * 100) }, '*');
  if (f >= 1) clearInterval(t);
}, 250);
```

## What happens at 100%

By default FiveM closes the loading screen when the game is ready to spawn the player. If your framework shows a character selector, there may be a gap between the bar reaching the end and the screen closing. You can keep the loading screen up until your own scripts are ready with manual shutdown — see [loadscreen_manual_shutdown](https://fam-tools.com/guides/fivem-loadscreen-manual-shutdown).

## Questions

### How do I make a progress bar on a FiveM loading screen?

Listen for `message` events on `window` and use `event.data.loadFraction` from the `loadProgress` event (0 to 1) to set the bar’s width.

### Why does my loading bar jump backwards?

Different events report progress for different phases. Keep the highest value shown so far and never set the bar lower.

### Can I show which resource is loading?

Show the `message` from the `onLogLine` event. It describes what the client is currently doing.

### Why does the bar reach 100% but the screen stays?

The game is ready but spawning has not finished, or a resource uses manual shutdown. The screen closes when the loading process or your script shuts it down.

### How do I test loading events outside FiveM?

Post fake messages with `window.postMessage({ eventName: 'loadProgress', loadFraction: 0.5 }, '*')` from the browser console.

## Related

- https://fam-tools.com/guides/fivem-loadscreen-manual-shutdown
- https://fam-tools.com/guides/fivem-loading-screen-handover-data
- https://fam-tools.com/guides/fivem-loading-screen-html-tutorial
- https://fam-tools.com/guides/stuck-on-loading-screen
