FiveM Loading Screens Explained: From HTML to the Real loadProgress Event

Your loading screen is the first thing every player sees, and most servers ship one with a fake progress bar. Here is how the real loading pipeline works.

By The OrbAugust 5, 20265 min read
Share
Futuristic loading bar glowing cyan with an amber leading edge over a night city skyline

A player's first sixty seconds on your server are a loading screen. Before your economy, your MLOs and your carefully balanced jobs, they see this one page. Most servers ship a template with music that autoplay policies silently block, a progress bar animated with CSS that finishes three times before the actual load does, and a YouTube embed that stutters the download it is supposed to entertain.

None of that is necessary. The loading screen API is small and well behaved. This guide covers how it actually works, how to hook the real progress events, and the mistakes that make loading feel longer than it is.

How the loading screen is wired

A loading screen is a normal web page declared in your resource manifest:

lua
-- fxmanifest.lua
fx_version "cerulean"
game "gta5"

loadscreen "index.html"

files {
    "index.html",
    "style.css",
    "app.js",
    "bg.webp"
}

When a player connects, the client downloads the resource, opens index.html in the NUI browser, and keeps it on screen while the actual game load happens behind it. The page is plain HTML, CSS and JavaScript in an embedded Chromium, so anything a website can do, your loading screen can do.

Two manifest directives change its lifecycle:

lua
-- keep the page interactive (mouse input) during load
loadscreen_cursor "yes"

-- do NOT auto-close when the game finishes loading;
-- a client script decides when to shut it down
loadscreen_manual_shutdown "yes"

The real progress events

The game posts lifecycle messages into the page as standard message events. Listening to them is one function:

js
// app.js inside the loading screen
window.addEventListener("message", (e) => {
    const data = e.data;

    switch (data.eventName) {
        case "loadProgress":
            // the only number you need: 0.0 to 1.0 real progress
            setBar(data.loadFraction);
            break;

        case "startInitFunction":
            // an engine init stage began, e.g. INIT_BEFORE_MAP_LOADED
            setStatus("Starting " + data.type);
            break;

        case "startDataFileEntries":
            // the game knows how many data files it will mount
            totalFiles = data.count;
            break;

        case "performMapLoadFunction":
            setStatus("Loading world " + (++mapLoads));
            break;

        case "onLogLine":
            // raw loader log lines, useful for a terminal-style screen
            appendLog(data.message);
            break;
    }
});

function setBar(fraction) {
    document.querySelector("#bar").style.width =
        Math.round(fraction * 100) + "%";
}

loadProgress with its loadFraction field is the one that matters. It is the engine's own measure of load completion. A bar driven by it fills exactly once, at the true pace of the load, on every machine. The CSS-animated fake bar that ships with most templates is not just cosmetic dishonesty: players learn that the bar means nothing and start alt-tabbing, and some kill the client mid-load thinking it hung.

Manual shutdown, done right

By default the loading screen closes as soon as the core game load ends. On roleplay servers that is almost always too early: the framework still has to fetch the character, apply appearance, and place the player. The result without manual shutdown is an ugly gap where players see the raw world, sometimes falling through the map, before your spawn logic runs.

With loadscreen_manual_shutdown "yes" in the manifest, a client script closes the screen when your server is actually ready:

lua
-- client/main.lua of your core resource
AddEventHandler("playerSpawned", function()
    -- appearance applied, coords set, camera ready
    ShutdownLoadingScreen()
    ShutdownLoadingScreenNui()
end)

The pairing matters: ShutdownLoadingScreen ends the engine's load phase, and ShutdownLoadingScreenNui removes the HTML page itself. Call the NUI shutdown only when everything the player should not see is finished, for example after your multicharacter UI has faded in. If the screen never closes, you forgot this call; it is the single most common loading screen bug report.

warning

While the loading screen NUI is up with manual shutdown, it can swallow input focus. If your character selector opens underneath it and clicks do nothing, shut the loading screen NUI down first, then open your selector.

The mistakes that make loading feel slow

Autoplay audio that never plays. Chromium blocks autoplay with sound. Your music starts muted or not at all, and on some setups the blocked request logs errors every second. Fix: start muted and offer an unmute button, or start audio after the first user interaction, which loadscreen_cursor "yes" makes possible.

Video backgrounds competing with the download. A 100 MB 4K loop looks great in the showcase and terrible on a player's 20 Mbps connection, where it steals bandwidth from the actual asset download it is decorating. Keep video under 10 MB, 1080p, heavily compressed, or use a static WebP and spend the bandwidth on the server content itself.

Remote assets. Fonts from Google, images from Imgur, scripts from a CDN. Every remote dependency is a point of failure on connections that are slow, filtered or offline-cached. Bundle everything into the resource; the manifest files list exists for exactly this.

Interactive pages that cost CPU. Particle systems and WebGL shaders run on the same machine that is trying to load a few gigabytes of assets. Keep the page light; the player's disk and CPU are busy.

Lying progress bars. Covered above. Wire loadFraction and delete the CSS animation.

What belongs on a good loading screen

  • Real progress from loadProgress
  • Server identity: name, logo, art that sets the tone
  • Useful content while waiting: rules summary, Discord link, keybind cheatsheet, staff list
  • Music with a visible mute toggle, defaulting to off or quiet
  • A log line or stage indicator so technical players can see it is not stuck

Build it by hand with the snippets above, or skip the plumbing: our Loading Screen Creator generates a finished, manifest-ready loading screen with the real progress wiring, audio controls and your branding, exportable as a drop-in resource.

The loading screen hands off to the next first impression, character selection. If that screen is a framework default, our comparison of multicharacter scripts covers the options, including what a good spawn selector adds. And if the load itself takes forever, heavy interiors are a usual suspect: see shells versus real MLOs for why some interiors stream so much data.

FAQ

Why does my loading screen close before players spawn?

You are missing loadscreen_manual_shutdown "yes" in the manifest, or nothing calls ShutdownLoadingScreenNui(). Add the directive and shut the screen down from your spawn logic once the character is placed.

Can the loading screen show per-player data like names?

Not directly: the page loads before your server scripts can talk to it. What you can do is pass static config through the resource files, or use onLogLine and the init events to show connection stages. Anything personalized has to wait for a post-load UI.

Does a heavy loading screen slow down the actual download?

Yes. The page shares bandwidth, CPU and disk with the asset streamer. Big videos and busy WebGL scenes measurably extend load times on weaker machines, which is the opposite of the screen's job.

Do I need loadscreen_cursor?

Only if the page is interactive: unmute buttons, tabs with server info, a scrollable rules panel. Pure visual screens can skip it, and skipping it avoids the screen capturing input it does not use.

Keep reading