Along with @Amiaa16’s method, another way to achieve this effect (that doesn’t require creating or getting any new speakers or touching channels) is through ChatService:RegisterFilterMessageFunction
.
We’ll also directly add a ModuleScript to our own ChatModules folder inside the Chat service, which avoids having to yield for a second or two while waiting for “ChatServiceRunner” to be parented to ServerScriptService:

You can get these objects by copying and pasting them from a test play and removing the extra instances, then adding a ModuleScript named “Disguise” (or any other fitting name). Make sure that InsertDefaultModules is enabled. ModuleScripts parented to ChatModules are expected to return functions, which are invoked once when the chat system is initialised. The function is passed the ChatService object, and is simply given the chance to register functions during initialisation. They serve no other purpose.
The function we need is used as void ChatService:RegisterFilterMessageFunction(string functionId, func func)
. It registers a function to the chat identified by functionId used as a filter that is invoked with every sent message. The function is passed the speaker’s name, the ChatMessage object, and the ChatChannel the message originated in. Any changes to the message will persist and be displayed when the message makes it through all of the other filter functions:
-- void run(table chatService)
local function run(chatService)
chatService:RegisterFilterMessageFunction("Disguise", function(speakerName, message)
-- ...
end)
end
return run
We’ll create a dictionary with keys as the original speaker names and values as the disguise names for simplicity’s sake. The admin system should edit this dictionary (or use something else entirely) accordingly:
-- Dictionary<string,string>
local nameDisguises = {
["Dandystan"] = "Kiriot22",
["LordHammy"] = "CleverSource",
["ThatUnfitBuck"] = "Dandystan",
}
If a speaker called Dandystan sends a message, their name will be changed to Kiriot22. We’ll index this dictionary within the filter function to check if the speaker is disguised. We’ll just return from the function if there’s no disguise:
-- find disguise
local disguise = nameDisguises[speakerName]
if not disguise then return end
Then, all we have to do is change the ChatMessage.FromSpeaker
to the disguise name, and change the message’s name colour to the colour associated with the new name (yes, each player name has a colour that never changes). To change the speaker name:
-- change name
message.FromSpeaker = disguise
To change the colour appropriately, we’ll directly edit ChatMessage.ExtraData
to change its "NameColor"
element using an algorithm found in the chat system (that’s cleaned up a bit
):
-- Array<Color3>
local nameColors = {
Color3.fromRGB(253, 41, 67), -- BrickColor.new("Bright red").Color,
Color3.fromRGB(1, 162, 255), -- BrickColor.new("Bright blue").Color,
Color3.fromRGB(2, 184, 87), -- BrickColor.new("Earth green").Color,
BrickColor.new("Bright violet").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Light reddish violet").Color,
BrickColor.new("Brick yellow").Color,
}
-- int getNameValue(string name)
local function getNameValue(name)
local value = 0
for index = 1, #name do
local cValue = name:sub(index, index):byte()
local reverseIndex = #name - index + 1
if #name % 2 == 1 then
reverseIndex = reverseIndex - 1
end
if reverseIndex % 4 >= 2 then
cValue = -cValue
end
value = value + cValue
end
return value
end
-- ...
message.ExtraData.NameColor = nameColors[getNameValue(disguise) % #nameColors + 1]
Putting everything together, the code should look like this:
-- variables:
-- Dictionary<string,string>
local nameDisguises = {
["Dandystan"] = "Kiriot22",
["LordHammy"] = "CleverSource",
["ThatUnfitBuck"] = "Dandystan",
}
-- Array<Color3>
local nameColors = {
Color3.fromRGB(253, 41, 67), -- BrickColor.new("Bright red").Color,
Color3.fromRGB(1, 162, 255), -- BrickColor.new("Bright blue").Color,
Color3.fromRGB(2, 184, 87), -- BrickColor.new("Earth green").Color,
BrickColor.new("Bright violet").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Light reddish violet").Color,
BrickColor.new("Brick yellow").Color,
}
-- functions:
-- int getNameValue(string name)
local function getNameValue(name)
local value = 0
for index = 1, #name do
local cValue = name:sub(index, index):byte()
local reverseIndex = #name - index + 1
if #name % 2 == 1 then
reverseIndex = reverseIndex - 1
end
if reverseIndex % 4 >= 2 then
cValue = -cValue
end
value = value + cValue
end
return value
end
-- void run(table chatService)
local function run(chatService)
chatService:RegisterFilterMessageFunction("Disguise", function(speakerName, message)
-- find disguise
local disguise = nameDisguises[speakerName]
if not disguise then return end
-- change name
message.FromSpeaker = disguise
-- handle name colour
message.ExtraData.NameColor = nameColors[getNameValue(disguise) % #nameColors + 1]
end)
end
return run
https://gyazo.com/a0e564a599e66d694fbe0b74eb818610