Custom Bubble Chat Text Color Issue

  • What do I want to achieve?
    I want to achieve a custom bubble chat that allows me to change the text color using a message tag, such as <RED> hi to display hi in red.

  • What is the issue?
    The issue is that when I send a message like <RED> hi, it shows the entire string <RED> hi instead of changing hi to red.

Meant to be:
image

But is:
image


  • What solutions have I tried so far?
    I have tried using Color3, using tables, and not using tables, and experimenting with hex codes. I believe the issue is with detecting the effect tag. It works fine with size effects like # or ###, but for the color effects, nothing seems to work… There are no error messages in the Output window as well…

Full script:

local TextService = game:GetService("TextService")
local ChatService = game:GetService("Chat")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local Chat = game:GetService("TextChatService")
local Event = ReplicatedStorage:FindFirstChild("ChatRemote")

local CommandsPrimaryAlias = {
	"/clear",
	"/console",
	"/emote",
	"/help",
	"/mute",
	"/team",
	"/unmute",
	"/version",
	"/whisper"
}

local CommandsSecondaryAlias = {
	"/cls",
	"/e",
	"/? ",
	"/m",
	"/t",
	"/um",
	"/v",
	"/w"
}

local Effects = {
	"# ",    -- Bold + 30 Font size
	"## ",   -- Bold + 25 Font size
	"### ",  -- Bold + 20 Font size
	"#### ", -- Bold + 15 Font size
	"##### ", -- Bold + 10 Font size
	"###### ", -- Bold + 5 Font size
	"<RED> ", -- Make the text red 
	"<ORANGE> ", -- Make the text orange
	"<YELLOW> ", -- Make the text yellow
	"<GREEN> ", -- Make the text green
	"<LIGHT BLUE> ", -- Make the text blue
	"<BLUE> ", -- Make the text blue
	"<PURPLE> ", -- Make the text purple
	"<PINK> ", -- Make the text pink
	"<BROWN> ", -- Make the text brown
	"<BLACK> ", -- Make the text black
	"<GOLD> ", -- Make the text gold
	"<SILVER> ", -- Make the text silver
	"<BRONZE> ", -- Make the text bronze
	"<RAINBOW> " -- Make the text rainbow
}

local colorMapping = {    
	"#FF0000", -- Red
	"#FFAA00", -- Orange
	"#FFFF00", -- Yellow
	"#00FF00", -- Green
	"#00FFFF", -- Light Blue
	"#0000FF", -- Blue
	"#8000FF", -- Purple
	"#FF55FF", -- Pink
	"#322100", -- Brown
	"#000000", -- Black
	"#FFD700", -- Gold
	"#C0C0C0", -- Silver
	"#CD7F32", -- Bronze
	nil -- Rainbow
}

local allAliases = {}
for _, alias in ipairs(CommandsPrimaryAlias) do
	table.insert(allAliases, alias:lower())
end
for _, alias in ipairs(CommandsSecondaryAlias) do
	table.insert(allAliases, alias:lower())
end

Players.PlayerAdded:Connect(function(player)
	player.Chatted:Connect(function(message)
		local sound = script.SFX
		local character = player.Character
		local head = character and character:FindFirstChild("Head")
		local all_sounds = {}

		local lowerMessage = message:lower()

		local isCommand = false
		for _, alias in ipairs(allAliases) do
			if lowerMessage:sub(1, #alias) == alias then
				local charAfterAlias = lowerMessage:sub(#alias + 1, #alias + 1)
				if charAfterAlias == "" or charAfterAlias:match("%s") then
					isCommand = true
					break
				end
			end
		end

		if isCommand then
			return
		end

		local sound_group = Instance.new("SoundGroup")
		sound_group.Name = "ChatSounds"
		sound_group.Parent = head

		local existingBillboardGui = head:FindFirstChild("CustomChatBubble")
		local existingSoundGroup = head:FindFirstChild("ChatSounds")
		if existingBillboardGui and existingSoundGroup then
			existingBillboardGui:Destroy()
			existingSoundGroup:Destroy()
		end

		if not head then
			return
		end

		local success, filteredMessage = pcall(function()
			return TextService:FilterStringAsync(message, player.UserId):GetNonChatStringForBroadcastAsync()
		end)

		if not success or filteredMessage == "" then
			return
		end

		local canChat, errorMessage = ChatService:CanUserChatAsync(player.UserId)
		if not canChat then
			return
		end

		local privacySettings = player:GetAttribute("ChatPrivacySetting")
		if privacySettings == Enum.ChatPrivacyMode.NoOne then
			return
		end

		local billboardTemplate = ReplicatedStorage:FindFirstChild("CustomChatBubble")
		if not billboardTemplate then
			return
		end

		local billboardGui = billboardTemplate:Clone()
		billboardGui.Parent = head
		billboardGui.Adornee = head

		local textLabel = billboardGui:FindFirstChild("Text")
		if not textLabel then
			return
		end

		textLabel.Text = ""

		local bubbleTween = TweenService:Create(
			textLabel,
			TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut),
			{TextTransparency = 1}
		)

		local sizeEffectApplied = false
		local colorEffectApplied = false
		local textSize = 20 -- Default text size
		local textColor = "#FFFFFF"

		if lowerMessage:sub(1, 1 + 1) == Effects[1]:lower() then
			textSize = 30
			filteredMessage = filteredMessage:sub(#Effects[1] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 2 + 1) == Effects[2]:lower() then
			textSize = 25
			filteredMessage = filteredMessage:sub(#Effects[2] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 3 + 1) == Effects[3]:lower() then
			textSize = 20
			filteredMessage = filteredMessage:sub(#Effects[3] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 4 + 1) == Effects[4]:lower() then
			textSize = 15
			filteredMessage = filteredMessage:sub(#Effects[4] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 5 + 1) == Effects[5]:lower() then
			textSize = 10
			filteredMessage = filteredMessage:sub(#Effects[5] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 6 + 1) == Effects[6]:lower() then
			textSize = 5
			filteredMessage = filteredMessage:sub(#Effects[6] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, #Effects[7]) == Effects[7]:lower() then
			filteredMessage = filteredMessage:sub(#Effects[7] + 1)
			colorEffectApplied = true
			textColor = colorMapping[1]
		end

		textLabel.TextSize = textSize

		local length = string.len(filteredMessage)
		local displayedText = ""

		for i = 1, length do
			local char = filteredMessage:sub(i, i)
			local new_sound = sound:Clone()
			new_sound.Parent = sound_group
			table.insert(all_sounds, new_sound)

			local volume = 0.1
			local waitTime = 0.05

			if char:match("%a") then
				volume = char:match("%u") and 0.5 or 0.1
			elseif char == "," or char == "." or char == "!" or char == "?" then
				local j = i - 1
				while j > 0 do
					local previousChar = filteredMessage:sub(j, j)
					if previousChar:match("%a") then
						volume = previousChar:match("%u") and 0.5 or 0.1
						break
					end
					j = j - 1
				end

				if char == "," then
					waitTime = 0.25
				elseif char == "." or char == "!" or char == "?" then
					waitTime = 0.5
				end
			end

			new_sound.Volume = volume
			new_sound:Play()

			textLabel.Text = textLabel.Text .. char

			if i < length then
				local nextChar = filteredMessage:sub(i + 1, i + 1)
				if nextChar == "," then
					waitTime = 0.25
				elseif nextChar == "." or nextChar == "!" or nextChar == "?" then
					waitTime = 0.5
				end
			end

			displayedText = displayedText .. char

			if sizeEffectApplied then
				textLabel.Text = "<b>" .. displayedText .. "</b>"
			elseif colorEffectApplied then
				textLabel.Text = '<font color="'..textColor..'">' .. displayedText .. '</font>'
			elseif not sizeEffectApplied and not colorEffectApplied then
				textLabel.Text = displayedText
			end

			wait(waitTime)
		end

		wait(2)
		bubbleTween:Play()
		wait(bubbleTween.Time)

		billboardGui:Destroy()
		for _, sound in ipairs(all_sounds) do
			sound:Destroy()
		end
	end)
end)

The part I’m probably having issues with:

		local sizeEffectApplied = false
		local colorEffectApplied = false
		local textSize = 20 -- Default text size
		local textColor = "#FFFFFF"

		if lowerMessage:sub(1, 1 + 1) == Effects[1]:lower() then
			textSize = 30
			filteredMessage = filteredMessage:sub(#Effects[1] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 2 + 1) == Effects[2]:lower() then
			textSize = 25
			filteredMessage = filteredMessage:sub(#Effects[2] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 3 + 1) == Effects[3]:lower() then
			textSize = 20
			filteredMessage = filteredMessage:sub(#Effects[3] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 4 + 1) == Effects[4]:lower() then
			textSize = 15
			filteredMessage = filteredMessage:sub(#Effects[4] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 5 + 1) == Effects[5]:lower() then
			textSize = 10
			filteredMessage = filteredMessage:sub(#Effects[5] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, 6 + 1) == Effects[6]:lower() then
			textSize = 5
			filteredMessage = filteredMessage:sub(#Effects[6] + 1)
			sizeEffectApplied = true
		elseif lowerMessage:sub(1, #Effects[7]) == Effects[7]:lower() then
			filteredMessage = filteredMessage:sub(#Effects[7] + 1)
			colorEffectApplied = true
			textColor = colorMapping[1]
		end

		textLabel.TextSize = textSize

The tables with effects and color mapping:

local Effects = {
	"# ",    -- Bold + 30 Font size
	"## ",   -- Bold + 25 Font size
	"### ",  -- Bold + 20 Font size
	"#### ", -- Bold + 15 Font size
	"##### ", -- Bold + 10 Font size
	"###### ", -- Bold + 5 Font size
	"<RED> ", -- Make the text red 
	"<ORANGE> ", -- Make the text orange
	"<YELLOW> ", -- Make the text yellow
	"<GREEN> ", -- Make the text green
	"<LIGHT BLUE> ", -- Make the text blue
	"<BLUE> ", -- Make the text blue
	"<PURPLE> ", -- Make the text purple
	"<PINK> ", -- Make the text pink
	"<BROWN> ", -- Make the text brown
	"<BLACK> ", -- Make the text black
	"<GOLD> ", -- Make the text gold
	"<SILVER> ", -- Make the text silver
	"<BRONZE> ", -- Make the text bronze
	"<RAINBOW> " -- Make the text rainbow
}

local colorMapping = {    
	"#FF0000", -- Red
	"#FFAA00", -- Orange
	"#FFFF00", -- Yellow
	"#00FF00", -- Green
	"#00FFFF", -- Light Blue
	"#0000FF", -- Blue
	"#8000FF", -- Purple
	"#FF55FF", -- Pink
	"#322100", -- Brown
	"#000000", -- Black
	"#FFD700", -- Gold
	"#C0C0C0", -- Silver
	"#CD7F32", -- Bronze
	nil -- Rainbow
}

Any ideas how to fix this?

1 Like

3 days and yet no answers… Maybe a reply will get some attention…?

1 Like

Hello!I am not sure how I would create the bold effects, but I could try recommending on what to do with the colours.

So far as I know, if you enable a textlabel (which is what I assume you are using) and want to give it colours, you must enable the RichText property inside the textlabel.

In order to give the text some colours, you need to write the colour in an HTML format.

I would recommend removing the colorMapping table and combining it with the Effects table, making it:

local Effects = {
	['<RED>'] = "#FF0000",
...
}

For the bold texts, add a second table.

Now what we need to do, is find that specific text that changes the entire chat to a colour and go through every word (to change its colour).

This is easily done with this script:

local text = 'Hello, I am a super cool person!'

local Effects = {
	['<RED>'] = "#FF0000",
}

for Effect, Colour in Effects do
	if string.find(text, Effect) then
local formattedText = ''
		local splitText = string.split(text, Effect) -- Gets rid of all of the <RED>'s
		text = splitText[2] -- The rest of the text without the <RED>
		formattedText = '<font color="' .. Colour .. '">' .. text .. '</font>'
		break -- Makes sure that it doesn't manipulate the other texts (unless you want it otherwise)
	end
end

After this script, you could then do the same for the bold effects (So it doesn’t override with the colours)

If there are any issues, please let me know!

1 Like

Hello.

Unfortunately, I’m not really sure how to implement this into the current script, the way I tried to implement it didn’t really work, the text became sort of doubled. And by “doubled” I mean, when I say for example helloo, it writes helloohelloo.

image

image

When I say for example # woah bold, it writes woah bold#woah bold where the first part is bolded and the other is not, and the same is happening with all other effects.

image

image

Meanwhile the color effects still don’t work and there are no errors in the Output window.

Sorry for the long wait time, I appreciate your help.

What @ClutchedForYou means is that you can implement rich text by adding it to the string that the player sends. To achieve this you first need to convert the <RED> in front of the string to a hex color value, which in this case would be #FF0000. After that you combine the string with some extra text to activate that hex color onto the string, which in the case of <RED> hi would result in
<font color="#FF0000">hi</font>.

As an example, here’s a piece of code that would take care of that for you.

--put the formats table somewhere in the top of your script, at least above the code below
local formats = {
   ["<RED>"] = "#FF0000"
}

local function convertToCString(message)
   local split = string.split(message," ")
   local formatted = message
   if formats[split[1]] then
   	formatted=""
   	for i = 2,#split do
   		formatted=formatted...split[i].." "
   	end
   	formatted='<font color="'..formats[split[1]]..'">'..string.sub(formatted,1,#formatted-1).."</font>"
   end
   return formatted
end

print(convertToCString("<RED> Hello there!"))
1 Like