Creating customisable bubble chat colours with the old BubbleChat script


  1. Preface
  2. Setting Up
  3. Editing ExtraDataInitializer
  4. Editing BubbleChat
    4.1. Looking at where our chat is received
    4.2. Editing OnPlayerChatMessage
    4.3. Editing createPlayerChatLine and createChatLine
    4.4. Going through CreateChatLineRender
  5. Postface


Hey there, it’s been a while. I’m coming back to tutorial writing! With my return to tutorial writing, I’m continuing my talks on the Lua Chat System modification and extensibility. My hope is that I can write many tutorials about using the system so you can do cool things with chat!

If you perform a quick search on the forum, you’ll find that there are actually a lot of developers around here interested in knowing how to change the colours of a bubble chat. There are many different approaches to this problem as well as available resources that you can use. Here, I’d like to teach you a best-case scenario method of doing this using native APIs.

With this tutorial, I hope that I can show you how exactly you can modify the BubbleChat LocalScript so that you can modify bubble chat colours. You will need to know some things prior to reading this tutorial, such as how to enable bubble chat and how to fork. These are very important and without them, this tutorial won’t mean much for you. A bit of API knowledge comes handy as well: we focus in on ChatSpeaker’s ExtraData capacity and ChatMessage which contains chat metadata. The system will pass on the ChatSpeaker’s ExtraData to a ChatMessage or it can be directly modified!

PS: By bubble chat colours, I mean both the text and fill colouring!

This tutorial is designed to work with LCS version 0.8.2018.05.16, which is the latest version since the posting of this thread. Any part of this can quickly go out of date.

(back to top)

Setting Up

The two things you will need to fork are the BubbleChat LocalScript and the ExtraDataInitializer. I will briefly explain why we need each of these pieces before getting into how to set them up.

LocalScript BubbleChat

The BubbleChat LocalScript is responsible for rendering all chats as bubbles above a sender’s head if bubble chat is enabled. This tutorial does not cover how to enable bubble chat but links a resource that can do this while avoiding forking as we don’t need other settings majorly changed.

ModuleScript ChatModules/ExtraDataInitializer

The ExtraDataInitializer script is, as the name suggests, what sets up ChatSpeaker ExtraData. We use this module to control defaults: in other cases, developers might use this to attach their own custom initial data (e.g. staff or VIP tags) as well. We’ll need this to set up some default colours for our script to fall back on, otherwise we’d have to do extra work to account for missing defaults.

Now that you know what we need, we can get right into forking. The best way to do this is to start a new place file (Ctrl + N) and start a play session (F5). All the components of the Lua Chat System will be added to the Chat service, which we can take out. All these items should be present:


What we want is the BubbleChat LocalScript and the ExtraDataInitializer ModuleScript which resides in ChatModules. Copy these two items and then exit your play session (Shift + F5). When you come back to the Studio view, paste these two items in the Chat service. Your view should look like this if done correctly:


We’re not done yet though. We only want ExtraDataInitializer forked, but copying ChatModules will copy any other ChatModules as well. Open up the folder so you can see all the children. You will want to delete everything except InsertDefaultModules and ExtraDataInitializer. The InsertDefaultModules object will tell Roblox to fill for missing items that we have not forked, completing the installation.


When done, you should be left with just three objects.


We can now get into the code work that will help us be able to change bubble chat colours using the ExtraData system. Please note: you can perform this in any way you want that makes you comfortable, but will need to make changes based on what you do. For best practice, I will be using a table which enforces the need to change both text and colour and makes the grouping clear.

(back to top)

Editing ExtraDataInitializer

This is the easy part, so we can handle this first. If you’re working with a fresh installation of the Lua Chat System, no colour settings for bubbles should be present. All you’re going to need to do is add a line of ExtraData that defaults our text and fill colour values.

We will be adding our code to the onNewSpeaker function (line 129) as that is where the ExtraDataInitializer begins attaching initial data to our ChatSpeakers. I prefer to do this right above the Tags portion since it looks consistent with the name and window chat colours set beforehand.

if not speaker:GetExtraData("BubbleChatColors") then
	speaker:SetExtraData("BubbleChatColors", {
		Text =, 0, 0),
		Fill =, 1, 1)

If you wanted to do your own setups for ExtraData as well, that’s fine to roll with as well! The important part that needs to get across here is that in the end, no matter what method you pick, you will need to add keys to ExtraData via SetExtraData so that we can pull them later.

For the sake of confirming that what you’ve done works, I recommend using different colour values so that your chat colours are actually different when you start typing. When you’ve confirmed it works, you can change them back to the black text on white fill schema or apply your own, up to you! I will be using, 1, 1) for text and, 0, 0) for fill.

(back to top)

Editing BubbleChat

Here is where the tricky part comes into play. I got lost myself when I initially tried fiddling around with the script before I finally got around to making coloured bubble chats. It is easier if you work in terms of the logical flow rather than editing from top down so you don’t get lost as quickly.

Note: BubbleChat renders two types of chats - player chats and calls from Chat.Chat. We will only be working with text and fill colours from player chats. Calls from Chat.Chat apply the ChatColor Enum to change the ribbon colour. As these calls do not adhere to the ChatSpeaker API, it is much more difficult to work with them and you need to do the heavy lifting on your own. I may write a separate tutorial on changing bubble chat colours for NPCs so you can safely use Chat.Chat with different colours.

(back to top)

Looking at where our chat is received

Start by scrolling to the bottom. You will find two OnClientEvent connections: OnNewMessage and OnMessageDoneFiltering. These functions are identical but function differently. I haven’t looked into these too deeply, but I believe OnMessageDoneFiltering is for the client while OnNewMessage is for everyone else. To remain on the same page, we will edit both the same way.

We’re lucky that the Lua Chat System is very extensible and mod-friendly. A ChatMessage object is sent to the client, but the BubbleChat script only uses the Message key of it. So that we can access our ExtraData later, we’re going to instead pass the whole ChatMessage around.

At the bottom of each function of the remotes, you will see this line:

this:OnPlayerChatMessage(sender, messageData.Message, nil)

Erase the message part. We will be sending the entire object rather than just the message string to our relevant functions. We can later pull out the message string and colours for our own use.

this:OnPlayerChatMessage(sender, messageData, nil)

We’ve now handled the receiver. When a player chats, the ChatMessage is sent through a RemoteEvent. The BubbleChat LocalScript listens for this so it can catch the message and then process it for rendering above player’s heads. Let’s move down the logic path and jump to the OnPlayerChatMessage function at line 557.

(back to top)

Editing OnPlayerChatMessage

Next we have OnPlayerChatMessage, which is where our ChatMessage object was passed to. We’re going to do a little bit of modifying here. The function is currently expecting that we’ll be giving it a string, but we’ve changed it so it receives a table. We now need to account for this.

Start by making it clear that OnPlayerChatMessage should expect a messageData table by changing the parameter message to messageData. This isn’t a requirement and you can continue working with the message parameter, but I reserve that for the actual message I take out below and want to be very clear about what my function is doing.

The next few lines require a message to perform input sanitsation on, so we’re going to go ahead and extract our variables right away so they’re ready to go. The other keys of ChatMessage after this are fairly pointless and we end up dropping the object here after pulling our needed keys.

On an unrelated note, if you're interested in sanitisation. It's not related to the thread, but you may have had that question after seeing me mention the word.

Here’s a quick rundown on sanitisation and a related term, validation, in case there are any questions on that. They aren’t important to know in the context of this tutorial, so completely optional if you want to read on here. It’s good knowledge to have on hand but again, irrelevant.

Input sanitisation

The act of input sanitisation is accepting user input but only after you’ve performed scrubbing on the information. For example, say a user sends a 400 character message but our server limits messages to 200 characters. Instead of rejecting this message, we truncate (drop) all characters after the 200 character mark, then accept that through.

Input validation

The act of input validation is accepting only correctly formed data according to your formats and rejecting anything that is outside of these parameters (malformed data). For example again, our chat scenario. If a user sends a 400 character message when the limit is 200, we instead reject the message from being sent.

So the difference?

Sanitisation fits sent data to your format before sending it. Validation will reject it if it is incorrect. If you have used a guard clause for remote security or otherwise rejected the script from continuing functions if input fails a check, you’ve done validation. If you have an input area that imposes a maximum amount of characters allowed, you’ve done sanitisation.

Awesome! Let’s get back to the tutorial. Set up the following code just above the message sanitisation:

local message = messageData.Message
local bubbleColors = messageData.ExtraData.BubbleChatColors

A note on the second variable: ChatSpeaker also passes its ExtraData to the ChatMessage object. This is useful if developers want to intercept chat metadata without using the speaker’s defaults (e.g. certain prefixes trigger the chat to be a different colour while a player’s chat is normally white), so we’ll want to access ExtraData in order to get our bubble chat colours.

And now for a picture of what your code should look like. As always, I’ve highlighted the new chunks we’ve added in green and our extra guiding information in purple - in this case, indicating where the input sanitisation happens. It’s important that the above two variables come before that call.

Now that we’ve properly tailored the OnPlayerChatMessage function to handle a ChatMessage table instead of a message, we’re good to go. We no longer care about the message from this point on and want to focus on changing the colours. Let’s move on.

The next part we’re going to want to do is look at createPlayerChatLine. This function is responsible for handling metadata for the chat message. Naturally, we are also going to add our colours here so that when the chat line is sent around, the colours can be pulled from the metadata.

We’re going to send an extra argument here, the bubbleColors variable we defined earlier. I prefer to have this sent after the sanitised message is sent because it makes sense this way when you’re reading your code: first you deliver the message and then the colours. Parameter order matters here, so if you want to to make it the last parameter then make sure you’re keeping track of that.

This line:

local line = createPlayerChatLine(sourcePlayer, safeMessage, not fromOthers)

Will become this:

local line = createPlayerChatLine(sourcePlayer, safeMessage, bubbleColors, not fromOthers)

After this, we are done modifying OnPlayerChatMessage. To refresh: createPlayerChatLine will create a chat line which is actually just metadata associated with our message for later rendering. This object is passed around, so we’ll have our colours readily available for use later in regards to where it’s sent, CreateChatLineRender. We will revisit this later: for now, we need to go to createPlayerChatLine to account for the new parameter and to inject our colours to the line data. To line 181!

(back to top)

Editing createPlayerChatLine and createChatLine

Welcome to createPlayerChatLine, where we will now inject our received colours from the parameters.

Fun fact: createPlayerChatLine is actually a wrapper function for createChatLine. createChatLine is also used by other functions, so createPlayerChatLine wraps it to attach player information to the chat line for future reference such as the player’s name. createChatLine is called and then the table is returned to createPlayerChatLine if we want to add extra player info. In our case, as player input will trigger the need to use a different chat colour.

With that in mind: so we can keep createChatLine usable by other functions (specifically Chat.Chat which is the only other natively supported function that can cause bubble chats), we’re going to add our colours from here instead of from the root function, but so that we don’t have to do heavy lifting later, we’re also going to quickly modify createChatLine to create some defaults.

First of all, add a bubbleColors parameter beside the message one so we can shift our parameters down and accept the colours. Straight forward, pictured example if you need it.

Recall what bubbleColors is. It is the “BubbleChatColors” table that we extracted from the ChatMessage object a while back. This is a table containing two keys: Text and Fill. this is a table. With that in mind, we will be placing our two keys from bubbleColors into this. ChatMessage after this becomes wholly irrelevant to us.

if bubbleColors then
	this.TextColor = bubbleColors.Text
	this.FillColor = bubbleColors.Fill

* Note: While we know for a fact that bubbleColors will be non-nil, just to be on the safe side you’ll want to account for the incident that we do not receive bubbleColors.

Now move up to the function before this, which is the root createChatLine. We will be setting our default bubble chat colours, black text and white fill, here. Going forward, we will have the BubbleChat LocalScript refer to these fields in the chat line table to determine the colour of a chat and make it easier for us to integrate different colours. Possibly, in the future, NPC colours too!

Just like in the code above, add a TextColor and FillColor to the this table.

this.TextColor =, 0, 0)
this.FillColor =, 1, 1)

And then we’re done. We have successfully specified default colours that createChatLine will use when creating a chat line and our createPlayerChatLine function will, if it receives a bubbleColors parameter, modify these to the proper colours. We can move on to our final destinations.

createPlayerChatLine was called by OnPlayerChatMessage, so following back through the logic we find ourselves back at line 577 with the variable line. This is sent to the function CreateChatLineRender, so we will now be going there. Follow me to line 520, or wherever it is - the line count has definitely changed since we’ve added code.

(back to top)

Going through CreateChatLineRender

CreateChatLineRender is the primary function responsible for doing the displaying of chats. This is where we can see the results of our work pay off, with coloured chats. This function receives the chat line made earlier from createPlayerChatLine, so we just need to use that data to start modifying the way chats are set up.

Are you still with me? I hope!

We’ll start with the easy parts first and then move into more difficult grounds. The easy part is changing the colour of the text. Look for the function CreateBubbleText in CreateChatLineRender. Like at the beginning of the tutorial, this only deals in the message but we want to use more than just the message here. That being said, as this function is reused for a different purpose, we’ll need to send the message first and then the rest of the data later. This will also help us modify the distant chat bubble.

Find this:

local bubbleText = this:CreateBubbleText(line.Message, shouldAutoLocalize)

And make it this:

local bubbleText = this:CreateBubbleText(line.Message, line, shouldAutoLocalize)

Now find the actual CreateBubbleText function. Again, super easy to modify because this function only creates a TextLabel. We’ll be adding a second parameter which is the full table of message data that we can use later.

Now that this is done, we’re going to go and change the properties of the TextLabel using the information we have in chat line information. This is just standard property writing. As we already have access to the original message as a parameter, we’re only adding a new line - new text colour.

Done. Text colour is now changed. In the off chance that another function uses CreateBubbleText without passing the second and/or third argument, the if statement will catch that and prevent the text color from being changed. In our case, the only other time this is called is to make the distant chat ellipses, so we’ll revisit changing those colours later. That part is fairly difficult.

You can quickly test what you have so far. In my case, as I set my text to be pure white for testing purposes, my bubble should also be pure white if this works.

Let’s move on to colour the fill of the bubbles now. This part is also super easy, but dangerous. We’re assuming that the hierarchy of the chat bubbles will always remain the same. Any change to the chat bubble’s structure will break this code and it’s impossible to account for it arbitrarily, so we’ll need to move forward with the assumption that the bubble parent-child structure stays the same.

The actual rendered bubble will be an ImageLabel and the tail will be a child of it. We’re lucky that these images are pure white, because changing the image colour will exactly reflect the colours that we want. Add two lines to change the bubble’s colours. They must be done before setting it visible so that the changes register first, then it’s enabled.

chatBubbleRender.ImageColor3 = line.FillColor
chatBubbleRender.ChatBubbleTail.ImageColor3 = line.FillColor

And now let’s test again. If this works, I should have white text and a dark red chat bubble. This is nearly our final destination, as we will still have one more roadblock to overcome: the distance chat bubble. That part is much more difficult to work with!

Our finished result:

And our final problem:

The distance chat bubble is a much more difficult barricade to overcome. There is also only one chat line, so we can never keep the colours fully consistent. It’d be fine to leave it like this but you may be slightly annoyed by the inconsistency.

For this, I would create a new function entirely. It will make sure that the chat bubble assumes the last colour used for a chat. The BubbleChat LocalScript first calls a function to create a BillboardGui for bubble chat renders, so after that’s done we will run our update function.

Like how we changed the colour of the bubble and the tail earlier, we need to make various assumptions about the structure of the chat and that they will remain true. We will also need to follow some of the conventions seen throughout the script to get this done right.

Our main assumption is that the distance chat bubble will always remain “SmallTalkBubble”. Our second assumption is that SmallTalkBubble always exists, but is made visible or invisible depending on the camera’s distance from the character. Using these assumptions, we get to work.

I will hand over the code fully written with comments to guide you through what it is doing. I recommend putting this function under CreateBillboardGuiHelper so that you can find it right in around the same area as the actual billboard creation.

This is the function you will need:

--- Ran to modify the colour of the small talk bubble after-the-fact
-- Accepts a character and the message data
function this:UpdateBillboardGui(instance, line)
	-- This is how the BubbleChat script gets the BillboardGui for rendering chats
	local billboardGui = this.CharacterSortedMsg:Get(instance)["BillboardGui"]
	-- If the BillboardGui exists, then we'll proceed. It seems the above returns a table,
	-- where the key BillboardGui is set to the return value of createBillboardInstance
	-- (which literally returns a BillboardGui instance).
	if billboardGui then
		-- Find SmallTalkBubble, assuming the structure of the BillboardGui if non-nil
		-- is always the same (has a BillboardFrame container in it).
		local smallTalkBubble = billboardGui.BillboardFrame:FindFirstChild("SmallTalkBubble")
		if smallTalkBubble then
			-- Make wild assumptions about the structure remaining consistent.
			smallTalkBubble.ImageColor3 = line.FillColor
			smallTalkBubble.ChatBubbleTailFrame.ChatBubbleTail.ImageColor3 = line.FillColor
			smallTalkBubble.BubbleText.TextColor3 = line.TextColor

And you will place it right under the creation of the BillboardGui.

this:UpdateBillboardGui(instance, line)

Our long carnival ride of wild assumptions has brought us this glorious resolution:

* Note: Wouldn’t be surprised if this breaks. Let me know and I’ll try and look into a solution that better accounts for this possibility while still taking a novice-friendly approach. I don’t want to start adding third party libraries or anything complex to get this done.

And with that taken care of, you now have yourself a fully customisable bubble chat colouring system to work from. All you need to do is add bubble chat colours to players at your own whims by using SetExtraData and passing a table with the Text and Fill keys. You can also directly modify a ChatMessage’s ExtraData if you know how to do that. :stuck_out_tongue_winking_eye:

ChatSpeaker:SetExtraData("BubbleChatColors", {
	Text =, 0, 0),
	Fill =, 0, 0),

(back to top)


This has been my first tutorial in a long while, and I’ve really forgotten how fun it is to share my knowledge with the developer community to help better their works. I may have made a few mistakes or oversights. I’ve gone into as much depth as I could about this topic. I always tinker with the Lua Chat System every now and again, learning the API and how to customise it to fit a game’s needs.

On another note, I have created the #lua-chat-system tag. Although tags can be publicly used and so threads that also use this may blur out my own, I will be using this tag on all previous and new tutorials on the Lua Chat System. I plan to have a lot more tutorials going out about working with the Lua Chat System for those who want to continue to use it but also reap the benefits of it, rather than resorting to custom chat systems to have that same level of extensibility.

Let me know if you have any questions, comments, concerns or any other type of feedback. Let’s keep things constructive and on topic. Happy developing!

(back to top)


Demo File

Grab yourself a copy of the coloured bubble chat system created in this thread here:
Coloured Bubble Chat Demo.rbxl (33.7 KB)

EDIT NOTE: I believe there is a function use error where I use GetSpeakersList over GetSpeakerList. If that’s the case, please remove the plural to better test the function!

I’m attaching this in a separate post because the main post has become way too large and it takes forever for me to commit post changes. I also feel it’d be more appropriate to leave detached from the main thread since there isn’t really a good place to put it other than the postface.

While I was writing this tutorial, I was also actively editing the chat scripts, so it was like I was following along writing the tutorial and modifying the chat system at the same time. After I finished writing I just had a thought: why not release the place file?

If that’s too much text for you to go through (I tend to write very long tutorials haha), then you can skip all the steps and jump right to the finished product. You won’t learn much if you don’t go through the tutorial but I understand some readers may be on time constraints.

I have included an example of how to use this in the demo place file which I did not do a writeup on in the main thread. I know there’d be questions on how to use this if you skip the tutorial and go straight for the place file, so I also included a use demo so you can check out how to work this.


This is a brilliant & well detailed guide to use. I’ve had issues with the trying to change the bubble chat and this covers it all.

Thanks. :slight_smile:


Despite skipping through all the technical shizazzle, I applaud your dedication on this very detailed tutorial. Good job. :+1:


Haha, yeah these tutorials can tend to be very long.

Going through the whole thing is mostly if you’re interested in learning how to piece things together and get some practice done in modifying the system. In this case, since we need to modify and add code rather than just use API to our advantage, there’s a lot more that needs to go into it.

I’ve made sure to supply a place file at the end with the system that was created in this thread for readers under time constraints or who would rather get hands on experience, just with the lack of a technical explanation (and you don’t need one either, really). I’ll be sure to do that in the future as well for any creation tutorials I happen to make so readers aren’t forced to go through the whole thing to start playing around with the system.

Hope this will be of use!


Correct me if I’m wrong, but since the BubbleChat LocalScript is also being forked, wouldn’t it be easier to just add a few lines to that LocalScript to edit the chat colors instead of forking ExtraDataInitializer?

Edit: Just 3 added lines can change the background color and text color, and account for the distance chat bubble. Any particular reason why this method would be better?

1 Like

You can do this in virtually any way you want. That’s the beauty about the extensibility of the Lua Chat System, there’s many ways to mod the chat system and achieve the same end result. :slight_smile:

I’m using ExtraDataInitializer because it makes more sense (canonically) considering the module sets up the defaults for ExtraData. If you forego forking ExtraDataInitializer, then you also have to account for the lack of your colours being available by default in the BubbleChat LocalScript, meaning it’s doing more work than it realistically should.

You’d need to check for the existence of colour fields and if they don’t exist, inject defaults. The BubbleChat LocalScript should be kept only for performing the actual receiving and rendering work. It’s more preferable to avoid doing this work directly and follow usual expectations for configurations, which is that defaults are implicitly available and can be modified later into your code.

Do you have an idea of what minimal edits can be made without ExtraDataInitializer while allowing you to arbitrarily manage the colours of bubble chats? I’m wondering as you’ve mentioned you did this in three lines without it. Wondering if it would be beginner friendly as well.

This method is intended to be beginner and novice friendly as well as abide by convention, which is why I had it done this way - and why it’s better as well. ExtraDataInitializer already exists, so you can use that to your advantage rather than taking additional steps in BubbleChat.

The aforementioned 3 lines would just be:

Doing it like this would account for both the regular bubble:

and the distance bubble:


Thanks for sharing! This will better help me answer why my method is better.

The reason that my method of doing things is better is because it allows the developer to arbitrarily change the colours of a user’s chat bubble at their own whims using the ExtraData API supplied in the Lua Chat System. Like you can customise chat tags, this will allow you to do the same with bubble chat colours. The idea is making bubble colours a configurable element in the chat system.

In your case, by adding the three lines of code you did, you absolutely changed the bubble chat colours but not in the way this tutorial hopes to cover it in. The three lines of code you added only change the default colour schema of the bubble chat but don’t allow user-defined configurations.

In short: this method is better because it gives the users more options and preserves the default. So you can give, say, a special user some red chat, while other users retain the default colour schema.


Hey developers! Only 12 days of this tutorial’s creation and there’s news to share.

Developer Relations has just announced that changes are coming to the bubble chat system. Part of the change includes natively supported colour changing, among other customisation options that this system does not cover (e.g. font, bubble duration and more). Please check out the announcement here:

Unfortunately, as the new BubbleChat LocalScript is becoming a CoreScript, some developers may still want to use the old BubbleChat LocalScript. Any game still relying on BubbleChat from LCS version 0.8.2018.05.16 will still be able to use this tutorial.

I will not be providing anymore updates or maintenance on this tutorial or the product supplied in the second post, but you are welcome to continue to supply feedback or help other users by leaving a post below. I’ll also be around to offer support.

Thanks all!


Very useful tutorial. One question. How could I use different colors for different situations, ex. a whisper has a different color to be easily recognizable as a private message.

For this, you’ll be looking to fork the module responsible for handling whisper chats and then use the API that we create in this tutorial to your advantage. The chat module responsible for handling whispers is PrivateMessaging, so you can fork this module and place it into your ChatModules folder.

PrivateMessageReplicationFunction is the function in this module that is responsible for delivering whisper chats to other users. Once you find this, you’ll notice that there’s a line that deals with ExtraData. The ExtraData used here is from a ChatSpeaker object rather than a ChatMessage, so we do have to take steps to clean up after ourselves.

The simplest way is just to set our chat colours and then remove them afterward. In an ideal environment your whisper chats probably won’t need too much configuration other than differentiating them from a global chat in one colour.

Here’s what my exact PrivateMessageReplicationFunction implementation looks like for a game I develop for where I differentiate between global and private chats:

local function PrivateMessageReplicationFunction(fromSpeaker, message, channelName)
	local sendingSpeaker = ChatService:GetSpeaker(fromSpeaker)
	local extraData = sendingSpeaker.ExtraData
	extraData.BubbleChatColors = {
		Text ="Magenta").Color,
		Fill =, 1, 1)
	sendingSpeaker:SendMessage(message, channelName, fromSpeaker, extraData)

	local toSpeaker = ChatService:GetSpeaker(string.sub(channelName, 4))
	local fromSpeakerChannelId = GetWhisperChannelId(fromSpeaker)

	if (toSpeaker) then
		if (not toSpeaker:IsInChannel(fromSpeakerChannelId)) then
		toSpeaker:SendMessage(message, fromSpeakerChannelId, fromSpeaker, extraData)

	extraData.BubbleChatColors = nil

	return true

Doing it for TeamChat is way easier because it doesn’t take your current ExtraData into account, rather it composes a completely fresh dictionary and uses that for the message. For that, a simple fix of adding in the BubbleChatColors tables works.

local extraData = {
	NameColor = player.TeamColor.Color,
	ChatColor = player.TeamColor.Color,
	ChannelColor = player.TeamColor.Color,

	BubbleChatColors = {
		Text =, 1, 1),
		Fill = player.TeamColor.Color:lerp(, 0, 0), 0.25)
1 Like

Thank you! This is very useful, and works perfectly.

While I was experimenting, I did some tinkering, and found another way to do this, however, it is probably more complicated than need be.

I noticed OnMessageDoneFiltering and OnNewMessage have a channelName value, so I made a interesting way of doing this. I sent it to OnPlayerChatMessage and then made 2 new ExtraDataTypes that could be assigned through the server script. It also appears to work, but it is almost certainly worse for readability.

function this:OnPlayerChatMessage(sourcePlayer, messageData,channelName, targetPlayer)

if not this:BubbleChatEnabled() then return end

local localPlayer = PlayersService.LocalPlayer
local fromOthers = localPlayer ~= nil and sourcePlayer ~= localPlayer

local message = messageData.Message
local bubbleColors
if channelName == "All" then
	bubbleColors = messageData.ExtraData.BubbleChatColors
elseif channelName == "Team" then
	bubbleColors = messageData.ExtraData.TeamChatColors
else --Whispers are named "To (Recipient Name, so any other name would be a whisper)"
	bubbleColors = messageData.ExtraData.WhisperChatColors
local safeMessage = this:SanitizeChatLine(message)

local line = createPlayerChatLine(sourcePlayer, safeMessage, bubbleColors, not fromOthers)

if sourcePlayer and line.Origin then
	local fifo = this.CharacterSortedMsg:Get(line.Origin).Fifo
	--Game chat (badges) won't show up here
	this:CreateChatLineRender(sourcePlayer.Character, line, true, fifo, false)


1 Like

TIL you can shorthand it that way. I probably wouldn’t do it directly from there though because the tutorial mostly caters to arbitrary demands per project. One project may have more channels than just All and Team and whisper chats being used by default if a channel isn’t accounted for seems weird to do when instead the global chat should be the fallback/default.

1 Like

The chat colors broke with your script :sob:

@jaydensar’s method also doesn’t seem to work now either. I don’t know why.

I need you to share more information than just saying it broke. What did you do throughout this tutorial that caused it to break? Do you have code that you can share or a repro place file so I can investigate the problem myself? I need details if you want to receive help, I can’t just be told it broke.

1 Like

Yeah man. So the demo file you sent, the Enable to get new chat colours script, it errors on one of its lines.


In Studio - Play

Line 20

I think the ChatServiceRunner was updated, so the function wanting to be executed by your script are no longer possible on new versions of it.

Roblox did some updating on the BubbleChat localscript itself by the way. Something that was added is called, UserFixBubbleChatText.

local UserFixBubbleChatText do
local success, value = pcall(function()
return UserSettings():IsUserFeatureEnabled(“UserFixBubbleChatText”)
UserFixBubbleChatText = success and value

It appears 6 times through the BubbleChat script, which is in the functions that create the Gui Elements.

But besides that, I think the ChatServiceRunner was updated. I hope this helps you identify stuff in the future.

The bubble chat system itself does work, but just not the color changing. I’m not experienced enough with it, and that’s why I wanted to mention it.

Ah, that demo file is old. I actually misspelled the method there: it’s supposed to be GetSpeakerList, not GetSpeakersList. Remove the plural off of speaker and try again. I can’t remember why I had a plural there, I think it was a documentation or intellisense issue that gave the function to me that way once upon a time and I had just been using that.

If Roblox updated BubbleChat, you just need to follow the steps in the new script. I’ll see what I can do about updating the tutorial to account for the new code.

1 Like

I completely overlooked the post you sent with the file, my bad about that. Thanks for informing me of it again!