In short
In a Script in ServerScriptService, on every CharacterAdded, create a BillboardGui, set its Adornee to the character's Head, give it a Size in studs-friendly units and a StudsOffset of about 2 studs up, add a TextLabel with the player's DisplayName, and parent it to the Head. Set Humanoid.DisplayDistanceType to None to hide Roblox's own name above them.
- Make the tag on the server so every player sees the same tag.
- Use
Sizein scale (likeUDim2.fromScale(4, 1)) so the tag shrinks with distance, or offset (pixels) so it stays the same size on screen. - Decide titles on the server, from data players cannot change, such as group roles or game pass ownership.
Get the script
Overhead name tag script with titles
One server Script that puts a name and an optional title above every player's head with a BillboardGui and hides the default Roblox name.
-- Script in ServerScriptService named NameTags.
-- A name and an optional title above every player's head.
local Players = game:GetService("Players")
local MAX_DISTANCE = 60 -- studs; tags further away are hidden
type Title = { text: string, color: Color3 }
-- UserId -> title. Decided here on the server, where players cannot change it.
local TITLES: { [number]: Title } = {
[156] = { text = "Owner", color = Color3.fromRGB(255, 200, 60) },
}
local function makeLabel(name: string, text: string, color: Color3, height: number, y: number): TextLabel
local label = Instance.new("TextLabel")
label.Name = name
label.BackgroundTransparency = 1
label.Size = UDim2.fromScale(1, height)
label.Position = UDim2.fromScale(0, y)
label.Font = Enum.Font.GothamBold
label.Text = text
label.TextColor3 = color
label.TextScaled = true
local stroke = Instance.new("UIStroke")
stroke.Thickness = 1.5
stroke.Color = Color3.new(0, 0, 0)
stroke.Parent = label
return label
end
local function addTag(player: Player, character: Model)
local head = character:WaitForChild("Head", 10)
local humanoid = character:WaitForChild("Humanoid", 10)
if not (head and head:IsA("BasePart") and humanoid and humanoid:IsA("Humanoid")) then
return
end
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
local tag = Instance.new("BillboardGui")
tag.Name = "NameTag"
tag.Adornee = head
tag.Size = UDim2.fromScale(5, 1.4)
tag.StudsOffset = Vector3.new(0, 2.2, 0)
tag.MaxDistance = MAX_DISTANCE
tag.LightInfluence = 0
local title = TITLES[player.UserId]
if title then
makeLabel("Title", title.text, title.color, 0.4, 0).Parent = tag
makeLabel("PlayerName", player.DisplayName, Color3.new(1, 1, 1), 0.6, 0.4).Parent = tag
else
makeLabel("PlayerName", player.DisplayName, Color3.new(1, 1, 1), 0.6, 0.4).Parent = tag
end
tag.Parent = head
end
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function(character: Model)
addTag(player, character)
end)
if player.Character then
task.spawn(addTag, player, player.Character)
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end
RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts
Where to put it
ServerScriptService
NameTags Script new
Settings you can change
MAX_DISTANCEIn the script- Studs from the camera after which tags are hidden. Default:
60. TITLESIn the script- UserIds and the title and colour each one gets, such as Owner in gold.
Replace the UserId in TITLES with your own; it is the number in the address of your Roblox profile page.
Type-checked with luau-lsp strict against Roblox's API definitions on 5 November 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.
Prompt for RoCode
Add overhead name tags: a server Script that puts a BillboardGui above every player's head with their display name and, for the UserIds I list, a coloured title like Owner. Hide the default Roblox name and hide tags beyond 60 studs.
On this page
How an overhead tag works
A BillboardGui is drawn at its Adornee (a part) and turns to face every camera. Anything inside it, labels, images, frames, is ordinary GUI. Because the character is rebuilt every time a player respawns, the tag has to be added again for every new character, which is why the script listens to CharacterAdded.
| Property | Value in this guide | Why |
|---|---|---|
Adornee | The Head | Where the tag is drawn. Parenting it to the Head also sets this up. |
StudsOffset | 0, 2.2, 0 | Lifts the tag above the head instead of through it. |
Size | UDim2.fromScale(5, 1.4) | Scale on a BillboardGui is in studs, so the tag is 5 studs wide in the world. |
MaxDistance | 60 | Hide tags far away, so a crowded server is not covered in text. |
AlwaysOnTop | false | True would show tags through walls. |
LightInfluence | 0 | Keeps the text bright at night. |
Roblox already shows a name above each character. Humanoid.DisplayDistanceType set to None turns that off for one character, so only your tag is left. The health bar guide covers the default name and health display if you would rather keep and tune it.
Step 1: The name tag script
Hover over ServerScriptService, click +, insert a Script named NameTags and paste this. Put your own UserId in TITLES.
-- Script in ServerScriptService named NameTags.
-- A name and an optional title above every player's head.
local Players = game:GetService("Players")
local MAX_DISTANCE = 60 -- studs; tags further away are hidden
type Title = { text: string, color: Color3 }
-- UserId -> title. Decided here on the server, where players cannot change it.
local TITLES: { [number]: Title } = {
[156] = { text = "Owner", color = Color3.fromRGB(255, 200, 60) },
}
local function makeLabel(name: string, text: string, color: Color3, height: number, y: number): TextLabel
local label = Instance.new("TextLabel")
label.Name = name
label.BackgroundTransparency = 1
label.Size = UDim2.fromScale(1, height)
label.Position = UDim2.fromScale(0, y)
label.Font = Enum.Font.GothamBold
label.Text = text
label.TextColor3 = color
label.TextScaled = true
local stroke = Instance.new("UIStroke")
stroke.Thickness = 1.5
stroke.Color = Color3.new(0, 0, 0)
stroke.Parent = label
return label
end
local function addTag(player: Player, character: Model)
local head = character:WaitForChild("Head", 10)
local humanoid = character:WaitForChild("Humanoid", 10)
if not (head and head:IsA("BasePart") and humanoid and humanoid:IsA("Humanoid")) then
return
end
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
local tag = Instance.new("BillboardGui")
tag.Name = "NameTag"
tag.Adornee = head
tag.Size = UDim2.fromScale(5, 1.4)
tag.StudsOffset = Vector3.new(0, 2.2, 0)
tag.MaxDistance = MAX_DISTANCE
tag.LightInfluence = 0
local title = TITLES[player.UserId]
if title then
makeLabel("Title", title.text, title.color, 0.4, 0).Parent = tag
makeLabel("PlayerName", player.DisplayName, Color3.new(1, 1, 1), 0.6, 0.4).Parent = tag
else
makeLabel("PlayerName", player.DisplayName, Color3.new(1, 1, 1), 0.6, 0.4).Parent = tag
end
tag.Parent = head
end
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function(character: Model)
addTag(player, character)
end)
if player.Character then
task.spawn(addTag, player, player.Character)
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
for _, player in Players:GetPlayers() do
onPlayerAdded(player)
end
Each label fills part of the tag's height: the title the top 40 percent and the name the bottom 60 percent, with TextScaled making the text fit. UIStroke draws a dark outline so the text reads against bright skies. DisplayName is the name players chose to show; use player.Name instead for the unique username.
Step 2: Get the size right
The size of a BillboardGui works differently from screen GUIs, and it decides whether the tag looks right close up and far away.
| Size | Close up | Far away | Use it for |
|---|---|---|---|
Scale, UDim2.fromScale(5, 1.4) | Large | Shrinks like a real object | Name tags in most games |
Offset, UDim2.fromOffset(200, 50) | Same pixels | Same pixels, so it covers more of the world | Markers that must stay readable, such as a quest target |
With scale, the text becomes tiny at a distance, which is why MaxDistance hides it at 60 studs. With offset, tags of far-away players look huge compared to their bodies; keep MaxDistance low if you choose offset. To stop scaled text getting too big close up, add a UITextSizeConstraint to each label with a MaxTextSize such as 28.
Step 3: Titles from a group rank or a game pass
Instead of a hand-made list, the title can come from the player's role in your Roblox group or from a game pass they own. Both are read on the server. Put the check in a ModuleScript named Titles in ServerScriptService:
-- ModuleScript in ServerScriptService named Titles.
-- A title from the player's group role or a VIP pass, or nil.
local GroupService = game:GetService("GroupService")
local MarketplaceService = game:GetService("MarketplaceService")
local GROUP_ID = 0 -- your group's ID
local VIP_PASS_ID = 0 -- your VIP game pass ID
local function titleFor(player: Player): { text: string, color: Color3 }?
if GROUP_ID ~= 0 then
local ok, result = pcall(function()
return GroupService:GetRolesInGroupAsync(player.UserId, GROUP_ID)
end)
local topRole = ok and result.IsMember and result.Roles[1]
if topRole and topRole.Rank >= 200 then
return { text = topRole.Name, color = Color3.fromRGB(120, 200, 255) }
end
end
if VIP_PASS_ID ~= 0 then
local ok, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, VIP_PASS_ID)
end)
if ok and owns then
return { text = "VIP", color = Color3.fromRGB(255, 120, 220) }
end
end
return nil
end
return titleFor
GetRolesInGroupAsync returns IsMember and a Roles list ordered from the highest role down, because a member can now hold more than one role. Rank 200 and above is a common choice for staff; use your own group's numbers. Both calls go to Roblox's servers, so they are wrapped in pcall: if one fails, the player simply gets no title. The game pass guide shows how to sell the VIP pass and react when it is bought mid-game.
Then use it in NameTags, with two changes. At the top, under local Players = ..., add local titleFor = require(game:GetService("ServerScriptService"):WaitForChild("Titles")) :: (Player) -> Title?. In addTag, change the title line to local title = TITLES[player.UserId] or titleFor(player), so your hand-made list still wins and everyone else gets a group or VIP title when they have one.
Common overhead tag mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Tag made in a LocalScript | Only that player sees their own tag. | Make it in a server Script. |
| Tag added once on PlayerAdded | It disappears after the first death. | Add it on every CharacterAdded. |
| Default name left on | Two names float above the head. | Set DisplayDistanceType to None. |
AlwaysOnTop on | Tags show through walls, which gives away hiding players. | Leave it off unless that is the point. |
| Title chosen from data the client sends | Anyone can call themselves Owner. | Choose titles on the server from UserId, group roles or passes. |
Common errors and fixes
Open the Output window (Window menu, then Output) before you press Play. In Play mode you can see the tag under your character's Head in the Explorer.
I cannot see the tag
Check the Script is in ServerScriptService, then look under your character's Head in the Explorer during Play for a BillboardGui named NameTag. If it is there, its Size may be zero or the StudsOffset may put it inside the head. In first person you do not see your own tag at all.
The tag is huge or tiny
Change the scale numbers in Size (they are studs), or switch to offset as in Step 2.
The title never shows
Your UserId in TITLES is wrong, or for group titles GROUP_ID is still 0. Add print(player.UserId) in addTag to see your number.
Infinite yield possible on Head
Some custom characters have no part named Head. Adornee the tag to another part, such as HumanoidRootPart, and raise StudsOffset.
Build it with RoCode
If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the scripts and instances in your Explorer, so there is no code to paste.
Add overhead name tags above every player with their display name, and a blue title with their group role name for ranks 200 and up in my group, read on the server. Hide the default name and hide tags beyond 60 studs.
- Can look through your place first with Search Scripts, so it does not add a second tag system next to one you have
- Writes the Script with Create Script and is told to compile-check it with Check Script
- Sets an undo point before and after each batch of changes, so Ctrl+Z steps back one batch at a time
With plugin 3.9 or newer RoCode can run a short playtest and read the errors. A tag is easiest to judge by eye, so press Play and look at it from close up and far away. How RoCode connects to Studio.
Questions
How do I make a name above my head in Roblox Studio?
Create a BillboardGui with a TextLabel inside, set its Adornee to the character's Head and a StudsOffset of about 2 studs up, from a server Script on every CharacterAdded. Step 1 has the full script.
How do I hide the default Roblox name?
Set the character's Humanoid.DisplayDistanceType to Enum.HumanoidDisplayDistanceType.None.
How do I show a rank above a player's head?
Read their group role on the server with GroupService:GetRolesInGroupAsync and put the role name in a second label, as in Step 3.
Should I use DisplayName or Name?
DisplayName is what players choose to show and can be the same for several people. Name is their unique username. Many games show the display name and keep the username for admin tools.
Sources and further reading
- Roblox Creator Docs: BillboardGui. Adornee, Size in studs, StudsOffset, MaxDistance and AlwaysOnTop
- Roblox Creator Docs: Name and health display. DisplayDistanceType and the default name
- Roblox Creator Docs: GroupService:GetRolesInGroupAsync
- Roblox Creator Docs: UIStroke
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.