TutorialLoading ScreenDesign

How to Create a Custom FiveM Loading Screen

Complete guide to building and installing a custom loading screen for your FiveM server. Covers HTML/CSS design, music integration, server info display, progress bars, and performance optimization.

February 20, 2026·12 min read·RARX Network

Your loading screen is the very first thing players see when they join your server. It sets the tone, builds anticipation, and — if done well — keeps players engaged while assets load in the background. A generic or broken loading screen screams "default server," while a polished custom one says "this community cares about the details."

In this guide, we'll walk through the entire process of creating a custom FiveM loading screen from scratch: from the HTML/CSS skeleton to adding music, server information, dynamic progress indicators, and optimization techniques that keep load times fast.

How FiveM Loading Screens Work

A FiveM loading screen is simply a web page — an HTML file with CSS and optional JavaScript — displayed inside the game client while the server sends assets. FiveM uses a Chromium-based embedded browser (NUI), which means you can use modern HTML5, CSS3, and ES6+ JavaScript features.

The loading screen resource is structured like any other FiveM resource:

my-loadingscreen/
├── fxmanifest.lua
├── index.html
├── style.css
├── script.js
└── assets/
    ├── background.jpg
    ├── logo.png
    └── music.mp3

The fxmanifest.lua

Your manifest file needs to declare this resource as a loading screen:

fx_version 'cerulean'
game 'gta5'

description 'Custom Loading Screen'
author 'Your Server Name'

loadscreen 'index.html'

files {
    'index.html',
    'style.css',
    'script.js',
    'assets/*.jpg',
    'assets/*.png',
    'assets/*.mp3'
}
Important: The loadscreen directive tells FiveM to use this HTML file as the loading screen. Without it, your resource is just a normal script. Also note that loading screen resources use files instead of client_scripts.

Step 1: Building the HTML Structure

Start with a clean, minimal HTML5 document. Remember, this runs in an embedded browser — you don't need all the usual meta tags for SEO or viewport scaling. Keep it simple:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Loading...</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="loading-container">
        <div class="background"></div>
        <div class="overlay"></div>

        <div class="content">
            <img src="assets/logo.png" alt="Server Logo" class="logo">
            <h1 class="server-name">YOUR SERVER NAME</h1>
            <p class="tagline">The Ultimate Roleplay Experience</p>

            <div class="progress-section">
                <div class="progress-bar">
                    <div class="progress-fill" id="progressFill"></div>
                </div>
                <p class="loading-text" id="loadingText">Connecting to server...</p>
            </div>

            <div class="server-info">
                <div class="info-item">
                    <span class="label">Discord</span>
                    <span class="value">discord.gg/yourserver</span>
                </div>
                <div class="info-item">
                    <span class="label">Website</span>
                    <span class="value">yourserver.com</span>
                </div>
            </div>
        </div>
    </div>

    <audio id="bgMusic" loop autoplay>
        <source src="assets/music.mp3" type="audio/mpeg">
    </audio>

    <script src="script.js"></script>
</body>
</html>

Step 2: Styling with CSS

The CSS is where your loading screen comes to life. Here's a professional dark-themed stylesheet to start from:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
    font-family: 'Segoe UI', Arial, sans-serif;
    color: white;
}

.loading-container {
    position: relative;
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.background {
    position: absolute;
    inset: 0;
    background: url('assets/background.jpg') center/cover no-repeat;
    animation: slowZoom 30s ease-in-out infinite alternate;
}

@keyframes slowZoom {
    from { transform: scale(1); }
    to { transform: scale(1.1); }
}

.overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
}

.content {
    position: relative;
    z-index: 10;
    text-align: center;
    max-width: 600px;
    padding: 40px;
}

.logo {
    width: 180px;
    margin-bottom: 20px;
    filter: drop-shadow(0 0 20px rgba(201, 79, 79, 0.5));
}

.server-name {
    font-size: 2.5rem;
    font-weight: 700;
    letter-spacing: 4px;
    text-transform: uppercase;
    margin-bottom: 8px;
}

.tagline {
    color: rgba(255, 255, 255, 0.5);
    font-size: 0.95rem;
    margin-bottom: 50px;
}

.progress-bar {
    width: 100%;
    height: 3px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 2px;
    overflow: hidden;
    margin-bottom: 12px;
}

.progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, #c94f4f, #ec8c69);
    border-radius: 2px;
    transition: width 0.3s ease;
}

.loading-text {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.4);
    margin-bottom: 40px;
}

.server-info {
    display: flex;
    gap: 40px;
    justify-content: center;
}

.info-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.label {
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 2px;
    color: rgba(255, 255, 255, 0.3);
}

.value {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.7);
}

Adding Google Fonts

You can use Google Fonts in your loading screen, but there's a catch — the player may not have internet access during loading if they're connecting to a local server. For production servers, it's safer to download the font file and include it locally:

@font-face {
    font-family: 'Montserrat';
    src: url('assets/Montserrat-Bold.woff2') format('woff2');
    font-weight: 700;
    font-display: swap;
}

Step 3: JavaScript — Progress & Interactivity

FiveM sends loading progress events that you can listen to and use to update your progress bar. Here's a robust script that handles these events:

// Listen for FiveM loading events
const handlers = {
    startInitFunctionOrder(data) {
        document.getElementById('loadingText').textContent =
            `Loading ${data.type} scripts...`;
    },

    initFunctionInvoking(data) {
        document.getElementById('loadingText').textContent =
            `Executing ${data.name}...`;
    },

    startDataFileEntries(data) {
        document.getElementById('loadingText').textContent =
            `Loading ${data.count} data files...`;
    },

    performMapLoadFunction(data) {
        document.getElementById('loadingText').textContent =
            `Loading map (${data.idx}/${data.count})...`;
        updateProgress((data.idx / data.count) * 100);
    },

    onLogLine(data) {
        document.getElementById('loadingText').textContent = data.message;
    }
};

window.addEventListener('message', (event) => {
    const handler = handlers[event.data.eventName];
    if (handler) handler(event.data);
});

function updateProgress(percent) {
    const bar = document.getElementById('progressFill');
    bar.style.width = Math.min(percent, 100) + '%';
}

// Animated progress simulation for initial connection
let fakeProgress = 0;
const fakeInterval = setInterval(() => {
    fakeProgress += Math.random() * 2;
    if (fakeProgress >= 90) {
        clearInterval(fakeInterval);
        return;
    }
    updateProgress(fakeProgress);
}, 500);

// Music volume control
const music = document.getElementById('bgMusic');
if (music) {
    music.volume = 0.3; // Start at 30% volume
}
Pro tip: Don't rely solely on FiveM's progress events — they can be inconsistent depending on server configuration. The simulated progress gives players visual feedback that something is happening while real events provide accurate updates for the map loading phase.

Step 4: Adding Background Music

Music makes a massive difference. A good ambient track or lo-fi beat keeps players from alt-tabbing during loading. Here are the rules:

  • File format: Use MP3 for maximum compatibility. OGG works too but MP3 is safer.
  • File size: Keep it under 5MB. A 2-3 minute track at 128kbps is ideal. Larger files slow down the initial resource download.
  • Volume: Set it to 20-30% max via JavaScript. Players hate loading screens that blast music.
  • Licensing: Use royalty-free music. NoCopyrightSounds, Epidemic Sound, or Pixabay Audio are good sources.
  • Loop point: Choose a track that loops smoothly. Abrupt restarts are jarring.

For a mute/unmute button, add this to your HTML and JavaScript:

// In your HTML:
// <button id="muteBtn" class="mute-btn">🔊</button>

document.getElementById('muteBtn').addEventListener('click', function() {
    const music = document.getElementById('bgMusic');
    music.muted = !music.muted;
    this.textContent = music.muted ? '🔇' : '🔊';
});

Step 5: Installation and Configuration

Once your loading screen resource is complete:

  1. Place the folder in your server's resources/ directory
  2. Add ensure my-loadingscreen to your server.cfg — it should be one of the first resources loaded
  3. Remove or comment out any other loading screen resources
  4. Restart your server completely (not just the resource)
Important: Loading screen resources cannot be hot-reloaded with restart. You must fully stop and start the server (or use txAdmin's full restart) for changes to take effect.

Advanced: Adaptive Loading Screens

Want to take it further? Here are some advanced techniques used by top servers:

Slideshow Backgrounds

Rotate through multiple background images to keep the visual interesting during long loads:

const backgrounds = [
    'assets/bg1.jpg',
    'assets/bg2.jpg',
    'assets/bg3.jpg'
];
let currentBg = 0;

setInterval(() => {
    currentBg = (currentBg + 1) % backgrounds.length;
    const bg = document.querySelector('.background');
    bg.style.opacity = 0;
    setTimeout(() => {
        bg.style.backgroundImage = `url('${backgrounds[currentBg]}')`;
        bg.style.opacity = 1;
    }, 500);
}, 8000);

Live Player Count

You can fetch your server's player count and display it on the loading screen — though this requires the player to have internet access:

async function fetchPlayers() {
    try {
        const res = await fetch('https://servers-frontend.fivem.net/api/servers/single/YOUR_SERVER_ID');
        const data = await res.json();
        document.getElementById('playerCount').textContent =
            `${data.Data.clients}/${data.Data.sv_maxclients} Players Online`;
    } catch (e) {
        // Silently fail — player might not have internet during load
    }
}
fetchPlayers();

Performance Tips

A slow loading screen defeats its purpose. Keep these optimization rules in mind:

OptimizationWhy It Matters
Compress images to WebPBackground images are the biggest file — use WebP at 80% quality
Keep total resource under 10MBThe loading screen itself needs to download before it can display
Avoid external CDNsExternal requests can fail or add latency during server connection
Minimize JavaScriptHeavy JS can cause stuttering in the embedded browser
Use CSS animations over JSCSS animations are GPU-accelerated and smoother
No video backgroundsVideo files are huge and can cause memory issues in the NUI browser

Common Issues and Fixes

  • Loading screen doesn't show: Make sure loadscreen 'index.html' is in your fxmanifest.lua and all files are listed in the files block
  • Music doesn't play: Check the file path and ensure it's included in files. Also, some browsers block autoplay — FiveM's NUI usually allows it, but test thoroughly
  • Screen is blank/black: Check the browser console (F8 in FiveM) for errors. Usually a missing file or CSS syntax error
  • Images not loading: Paths are relative to the HTML file. Use assets/image.jpg, not /assets/image.jpg (no leading slash)
  • Loading screen persists after joining: This is a server-side issue — make sure your scripts call ShutdownLoadingScreenNui() when the player spawns

A well-crafted loading screen is one of the easiest ways to make your server feel professional. Take the time to design something unique, optimize it properly, and your players will notice the difference from the moment they connect. For premium FiveM resources to match your custom loading screen, check out our store.

RARX Network

Ready to upgrade your server?

Browse premium FiveM scripts, vehicles, and MLOs with free updates and 24/7 support.

Browse Store