Draw custom text labels on the GTA V minimap and pause map in FiveM - district names, custom POIs, faction turf tags, event banners, anything you want written directly on the map.
Full control per label: font, size, bold, RGB color, opacity, alignment, rotation and outline. Everything is drawn at runtime on a canvas - no pre-made images required.
- Custom text labels placed at any in-game world coordinate.
- 6 built-in system fonts out of the box (Arial, Verdana, Georgia, Courier New, Trebuchet MS, Comic Sans MS) - no
.ttffiles needed. - Custom
.ttf/.otffonts supported if you want your own branding (see Using custom fonts). - Per-text color (RGB), opacity, size, bold, alignment and rotation.
- Optional outline/stroke (its own color, opacity and thickness) for readability over any map background.
- Rendered on both the minimap and the pause (big) map.
- Simple exports API to add / remove / update / clear text labels at runtime.
Because DrawText is not rendered on the pause map, Off-MapText uses the runtime-texture overlay technique:
- For every text label, its position is projected into an offscreen browser (DUI) that renders it on a
<canvas>(transparent background) -html/text.html+html/text.js. - The DUI is converted to a runtime texture (
CreateRuntimeTextureFromDuiHandle). - The texture is placed on the map through a scaleform overlay (
MINIMAP_LOADER.gfx→ADD_SCALED_OVERLAY). - All labels share one atlas texture, rebuilt whenever a label is added, removed or updated.
- A recent FiveM server artifacts build.
MINIMAP_LOADER.gfxmust be present in the resource folder.
- Drop the resource folder into your server's
resourcesdirectory. - Make sure
MINIMAP_LOADER.gfxis in the folder (next tofxmanifest.lua). - Add it to your
server.cfg:ensure Off-MapText
The export/resource name is your folder name. If you keep the folder as
Off-MapText, useexports['Off-MapText']. Rename consistently if you use another folder name.
All settings live in config.lua.
| Option | Default | Description |
|---|---|---|
AtlasSize |
4096 |
Resolution (px) of the shared atlas texture holding every text label. Higher = sharper text, more VRAM. |
Pad |
25.0 |
Minimum world-unit margin around the text bounds. |
TextPadFactor |
1.0 |
Extra safety margin around labels, as a multiple of the largest font size in use. Prevents glyphs from being clipped by the atlas sprite. |
FlipY |
true |
Vertical orientation. Set to false if text appears mirrored top/bottom. |
Fonts |
see below | List of font family names available to use in font =. |
DefaultFont |
'Arial' |
Font used when a label omits font. |
DefaultSize |
32 |
Font size (px on the atlas) used when a label omits size. |
DefaultColor |
{255, 255, 255} |
{r, g, b} used when a label omits color. |
DefaultAlpha |
255 |
Fill opacity (0–255) used when a label omits alpha. |
DefaultAlign |
'center' |
'left' | 'center' | 'right', used when a label omits align. |
DefaultOutline |
true |
Whether labels get an outline by default. |
DefaultOutlineColor |
{0, 0, 0} |
Outline color used when a label omits outlineColor. |
DefaultOutlineAlpha |
255 |
Outline opacity used when a label omits outlineAlpha. |
DefaultOutlineWidth |
4 |
Outline thickness (px) used when a label omits outlineWidth. |
Config.Fonts = {
'Arial',
'Verdana',
'Georgia',
'Courier New',
'Trebuchet MS',
'Comic Sans MS',
}These are system fonts bundled with the CEF/Chromium browser FiveM uses for DUI - no font file to ship, they just work.
{
text = 'Elysian Island', -- the string to draw
font = 'Arial', -- must be one of Config.Fonts (or a custom font, see below)
size = 34, -- font size in px (on the atlas)
bold = true, -- optional, default false
color = { 70, 130, 220 }, -- { r, g, b } 0-255
alpha = 255, -- optional, overrides Config.DefaultAlpha
align = 'center', -- 'left' | 'center' | 'right'
rotation = 0, -- degrees
outline = true, -- optional, overrides Config.DefaultOutline
outlineColor = { 0, 0, 0 }, -- { r, g, b } 0-255
outlineAlpha = 200, -- 0-255
outlineWidth = 4, -- px
x = -150.0, -- world X
y = -1620.0, -- world Y
}Labels are 2D (X/Y position only). There is no hover/enter/exit detection - Off-MapText is a display-only resource.
All exports are client-side. Replace Off-MapText with your resource folder name.
Creates and renders a text label. Returns a numeric id (or nil if invalid).
local id = exports['Off-MapText']:addText({
text = 'Vespucci',
font = 'Georgia',
size = 30,
color = { 200, 70, 190 },
x = -1250.0,
y = -1050.0,
})Removes a label by id. Returns true if it existed.
exports['Off-MapText']:removeText(id)Replaces an existing label's data (position, text, style, ...) and redraws it. Returns true if it existed.
exports['Off-MapText']:updateText(id, {
text = 'Vespucci (contested)',
font = 'Georgia',
size = 30,
color = { 220, 40, 40 },
x = -1250.0,
y = -1050.0,
})Removes every label.
exports['Off-MapText']:clearTexts()test.lua is a minimal example: it loads every entry from Config.TestTexts through addText. Replace it with your own loader (database, server sync, admin command, etc.).
CreateThread(function()
for i = 1, #Config.TestTexts do
exports[GetCurrentResourceName()]:addText(Config.TestTexts[i])
end
end)The 6 built-in fonts require no setup because they're preinstalled in the CEF/Chromium engine FiveM uses for DUI. If you want your own .ttf/.otf font (a branded/stylized font for your server), you need to load it in the DUI page with a standard CSS @font-face. Two ways to do it:
- Create a
html/fonts/folder and drop your font file in it, e.g.html/fonts/MyFont.ttf. - Register it in
fxmanifest.luaso it's shipped to the client:files { 'MINIMAP_LOADER.gfx', 'html/text.html', 'html/text.js', 'html/fonts/MyFont.ttf' }
- Open
html/text.htmland load the font with@font-facein a<style>block:<style> @font-face { font-family: 'MyFont'; src: url('fonts/MyFont.ttf') format('truetype'); } html, body { margin: 0; padding: 0; background: transparent; overflow: hidden; } #c { display: block; background: transparent; } </style>
- Open
html/text.jsand make sure the font is loaded before the first draw, otherwise the canvas silently falls back to the default font on the first frame:const customFont = new FontFace('MyFont', 'url(fonts/MyFont.ttf)'); customFont.load().then(function (loaded) { document.fonts.add(loaded); draw(); // redraw once the font is actually ready });
- Add the family name to
config.luaso it shows up as a valid option:Config.Fonts = { 'Arial', 'Verdana', 'Georgia', 'Courier New', 'Trebuchet MS', 'Comic Sans MS', 'MyFont', }
- Use it on any label like any other font:
{ text = 'Custom Font!', font = 'MyFont', size = 32, x = 0.0, y = 0.0 }
If you'd rather not ship a separate file, you can inline the font directly as base64 inside the @font-face src, avoiding an extra network/file request:
<style>
@font-face {
font-family: 'MyFont';
src: url(data:font/ttf;base64,AAEAAAAL... ) format('truetype');
}
</style>This makes text.html heavier but keeps everything in a single file. Use an online "file to base64" converter or certutil -encode (Windows) / base64 (Linux/macOS) on your font file to get the string.
Whichever option you pick, custom fonts must be licensed for embedding/redistribution if you plan to share or sell the resource.
Off-MapText/
├─ fxmanifest.lua
├─ config.lua -- settings + Config.TestTexts
├─ test.lua -- example loader
├─ MINIMAP_LOADER.gfx -- required scaleform
└─ client/
├─ overlay.lua -- DUI -> runtime texture -> scaleform overlay engine
└─ client.lua -- text API (exports), atlas bounds & projection
└─ html/
├─ text.html -- DUI canvas page (shared atlas)
└─ text.js -- draws every text label, updated via SendDuiMessage
- All labels share one atlas texture. Every label is drawn into a single DUI (one Chromium process total, regardless of label count) and displayed as one scaleform sprite. Sharpness depends on how much of the map the labels span: labels spread across the whole map get fewer pixels each than labels clustered in one district. Raise
AtlasSize(e.g.8192) if text looks blurry once zoomed in, at the cost of more VRAM. - Adding or removing a label redraws the whole atlas. Changes made in the same frame are batched into a single rebuild, so adding many labels at once is cheap. Avoid adding labels one-per-frame in a loop.
- Labels are 2D. Only X/Y is used; there's no altitude/room separation.
- No interaction/events. Off-MapText only draws; it does not detect hover or player proximity (see MapZones if you need that).
- Author: OffSey