Text+|Custom fonts & advanced control

Text+ logo

An efficient, robust, open-source text rendering module for
Roblox, featuring custom fonts and advanced text control.

Module buttonGithub button



:art: More creativity!

Roblox developers suffer from a wide range of limitations.

This module aims to fix text limitations, allowing more
developers to unleash their creativity with text.

What it really does:

  • Custom fonts: Use any font file, whether completely custom or one found online.
  • Advanced control:
    • Easily transform and style any individual character, word or line.
    • Utilize awesome text styling like ‘justified’ alignments and spacing controls.

:eyes: Check it out below!

🎬 Showcases

Dialogue

A really simple ‘dialogue’, featuring:

  • Custom font.
  • Text appear and disappear animation.
  • ‘Shining’ word animation.

Text explosion

A neat text explosion effect. :exploding_head:
Features a custom font too.

:zap: Efficient, robust, intuitive.

  • Blazingly fast speeds.
  • Clear error feedback.
  • Typing & documentation.

:bulb: Learn how to use it!

🚀 Fundamentals

Getting the module

Let’s first get the module. There are two ways:

  • Get it from the creator store:
    • Click Get at the top of this post.
    • Click Get Model on the store.
    • Open the ToolBox in Roblox Studio.
    • Go to the Inventory tab.
    • Click on Text+.
  • Get it from GitHub:
    • Click Git at the top of this post.
    • Go to Releases.
    • Download the latest .rbxm file.
    • Find the file in your file explorer.
    • Drag the file into Roblox Studio.

Find a great place for the module in your explorer.

Basics

You’ll be creating text using frames as parents and boundaries, like this:

local textPlus = require(script.TextPlus)

local frame = path.to.frame -- You should actually get or create a frame.

textPlus.Create(
	frame, -- Parent and boundary.
	"This text is awesome!", -- Text.
)

The text will be wrapped inside of the frame.
Note that it will ignore any UIPadding.

You can get the text at any time like this:

textPlus.GetText(frame) -- Will return a string.

Line breaks

You can manually break lines by using multiline strings.
There are two ways to do this.

Use \n

You can use \n to break to the next line at any place.
\n will not be shown in the text.

"First line\nSecond line"

Use multiline strings

Using [[]] instead of "" will allow you to create a string that takes up multiple lines.
You simply make an actual line break, and it will work.

[[First line
Second line]]

Customization

The customization works with a table, where you can provide any customizations you want.
You don’t have to provide all, or even any, because they all have defaults.

You use it like this:

textPlus.Create(
	frame, -- Parent and boundary.
	"This text is awesome!", -- Text.
	{ -- Customization (optional).
		Size = 24,
		Color = Color3.fromRGB(255, 255, 255),
		XAlignment = Enum.TextXAlignment.Center,
		YAlignment = Enum.TextYAlignment.Center,
	}
)

Full list of customization options:

  • Size: number
  • Font: Font | CustomFont
  • Color: Color3
  • Transparency: number
  • Rotation: number
  • Offset: Vector2

  • StrokeSize: number
  • StrokeColor: Color3

  • ShadowOffset: Vector2
  • ShadowColor: Color3

  • LineHeight: number
  • CharacterSpacing: number

  • Truncate: “Tail” | “Break”

  • XAlignment: “Left” | “Center” | “Right” | “Justified”
  • YAlignment: “Top” | “Center” | “Bottom” | “Justified”

  • WordSorting: boolean
  • LineSorting: boolean

  • Dynamic: boolean


You can get the customization for a frame at any time like this:

textPlus.GetCustomization(frame) -- Will return a table.

This customization will always be valid, and will contain all customization options except false booleans and a select few customizations that are not required in the list.

Fonts

The font is one of the customization options, and works mostly like all of the others.

It takes in either:

  • A Font object, usually created with Font.new().
  • A custom font table. Refer to the Custom Fonts tutorial section.

You can use it like this:

textPlus.Create(
	frame,
	"This text is awesome!",
	{
		Font = Font.new(
			"rbxasset://fonts/families/Arial.json", -- Family.
			Enum.FontWeight.Regular, -- Weight.
			Enum.FontStyle.Normal -- Style.
		)
	}
)

Built-in fonts

You can find a lot of fonts on the documentation page.

Simply copy the asset id from the font list and paste it into the Font object’s Family (first argument).

Creator store fonts

Alternatively, browse many more fonts at the creator store.

Click Get Font.

Create a TextLabel in Roblox Studio and apply the font to it.

Make sure you have the TextLabel selected and run this in the command bar:

print(game.Selection:Get()[1].FontFace.Family)

It will output the asset id, that you need.
Simply copy and paste it into the Font object’s Family (first argument).

Custom fonts

If it’s still not enough, custom fonts offer endless possibilities.
But that has it’s own section (also in this post). Consider checking it out.

Modification

You can also modify a text frame after the creation. It’s done the same you create it to begin with:

textPlus.Create(
	frame, -- Frame that already has text created within it.
	"This text has been modified!" -- New text.
)

You can even modify the customization after the creation like this:

textPlus.Create(
	frame, -- Frame that already has text created within it.
	"This text has been modified!", -- New text.
	{ -- New customization (optional).
		Size = 12 -- Overwrite size.
		-- Everything else will stay like before!
	}
)

It will keep all previous customization options, only overwriting with those you provide in the table.

Text bounds

It will automatically calculate the text bounds when you create text.
You can always get the text bounds of a frame like this:

textPlus.GetTextBounds(frame) -- Will return a Vector2.


⚙️ Fine-control

Introduction

The sorting options in the customization are crucial to fine-control.
Using word and line sorting, you can not only modify individual characters, but whole words and lines.

Use the sorting customization options like this:

textPlus.Create(
	"Text",
	frame,
	{
		LineSorting = true,
		WordSorting = true
	}
)

By default, line and word sorting will both be off, meaning your frame will contain pure characters.

Note that everything is named numerically, relative to its parent.

Both lines and words will be sorted using folders:
Word & line sorting
(Both sortings enabled)

Full looping

It’s pretty simple to loop through. Just make sure you respect your sorting.
Here’s an example with both sortings enabled:

for lineNumber, line in frame:GetChildren() do
	print("Line "..lineNumber) -- "Line 1", "Line 2" etc.
	
	-- 'line' will be a folder.
	for wordNumber, word in line:GetChildren() do
		print("Word "..wordNumber) -- "Word 1", "Word 2" etc.
		
		-- 'word' will be a folder.
		for characterNumber, character in word:GetChildren() do
			print("Character "..characterNumber) -- "Character 1", "Character 2" etc.
			
			-- For Roblox fonts, 'character' will be a TextLabel.
			-- For custom fonts, 'character' will be an ImageLabel.
		end
	end
end

If you have only one of the sorting types enabled, there will only be one layer of folders, and you’ll have to do something like this:

for wordNumber, word in frame:GetChildren() do
	print("Word "..wordNumber) -- "Word 1", "Word 2" etc.
	
	-- 'word' will be a folder.
	for characterNumber, character in word:GetChildren() do
		print("Character "..characterNumber) -- "Character 1", "Character 2" etc.
		
		-- For Roblox fonts, 'character' will be a TextLabel.
		-- For custom fonts, 'character' will be an ImageLabel.
	end
end

Alternatively, you can use the GetCharacters function, which will return all of the characters.
This function is optimized, and respects all sorting types.
You can simply loop through the table like this:

local characters = textPlus.GetCharacters(frame)
for characterNumber, character in characters do
	print("Character "..characterNumber) -- "Character 1", "Character 2" etc.
	
	-- For Roblox fonts, 'character' will be a TextLabel.
	-- For custom fonts, 'character' will be an ImageLabel.
end

Specific indexing and looping

You can always access the exact line, word or character number you want by indexing like this:

frame["1"]

With some sorting enabled, you’ll be able to loop through specific lines and words.

Character (requires you to have no sorting)

for _, character in frame:GetChildren() do
	-- For Roblox fonts, 'character' will be a TextLabel.
	-- For custom fonts, 'character' will be an ImageLabel.
end

Word (requires word sorting)

local word = frame["1"] -- Word sorting.
local word = frame["1"]["1"] -- Line and word sorting.
for _, character in word:GetChildren() do
	-- For Roblox fonts, 'character' will be a TextLabel.
	-- For custom fonts, 'character' will be an ImageLabel.
end

Line (requires line sorting)

local line = frame["1"]

-- Line and word sorting:
for _, word in line:GetChildren() do
	-- 'word' will be a folder.
	for _, character in word:GetChildren() do
		-- For Roblox fonts, 'character' will be a TextLabel.
		-- For custom fonts, 'character' will be an ImageLabel.
	end
end

-- Line sorting:
for _, character in line:GetChildren() do
	-- For Roblox fonts, 'character' will be a TextLabel.
	-- For custom fonts, 'character' will be an ImageLabel.
end

Transform and style

All characters are instances, that can be directly modified.
As I’ve noted a few times already:

  • Roblox fonts: TextLabels
  • Custom fonts: ImageLabels

And don’t forget to be creative!


🛠️ Custom fonts

Font generation tool

We’re going to be using SnowB to generate the necessary font files.
It’s a free, simple bitmap font generation website.

Character selection

You’ll have to specify which characters you want to include in your custom font.
This is done at the Glyphs section in the top-left corner.

By default, there should be all the characters you’ll need.
But you might want to get rid of the character.
Why is it there? I don’t know.

If you’re going to be using any more characters, feel free to add them.

Font settings

Head over to the Font section, located right below Glyphs.

Get a .ttf or .otf file (a font file), whether it’s one of your own or one you found online.
Press ADD FONT FILE, and select your font file.

Choose the smallest possible Font Size that covers your largest intended use case. Avoid extreme values (like 1,000px) unless absolutely necessary, since the images have to be replicated from server to client.

Fill settings

Head over to the Fill section, located in the top-right corner.

Set Color to white. Make sure it’s fully white (255, 255, 255, 100).

Export the files

Click Export in the top bar.
Input a File Name for your font.
For the Export Type, select BMFont XML.

Press Save in the bottom-right corner of the pop-up.
It should save a .xml and a .png file.

Convert XML to Lua format

I’ve made this step nice and simple with a program that does this for you.

Download my converter on GitHub.
Unzip/extract the zip file.
You should get a folder with a Convert.exe file inside.

Drag your .fnt file into the folder where the Convert.exe file is.

Then simply open the Convert.exe file.
It might warn you, saying it might be a threat (virus). This is simply because I haven’t registered the file, as it’s extremely expensive to do so. The code is open-source.

After a few short seconds, you should see a new .lua file.

Upload the image and get its id

Upload the image at the creator hub.

Then go to your images.
Find your image, click the three dots on it, and then click Copy Asset ID.

Import font data to Text+

Find the TextPlus module in your explorer.
Open the CustomFonts module located inside of it.

Add your font like this:

return {
	MyFont = -- Paste Lua XML file content here.
}

Add your image id for the font in the same table as the Size and Characters, like this:

MyFont = {
	Image = 0, -- Image id.
	-- From Lua XML file:
	Size = 32,
	Characters = {
		
	}
}

For fonts that have multiple weights and/or styles, it’s recommended to use the following format:

return {
	-- Fonts.
	MyFont = {
		-- Weights.
		Bold = {
			-- Styles.
			Italic = {
				Image = 0, -- Image id.
				-- From Lua XML file:
				Size = 32,
				Characters = {
					
				}
			}
		}
	}
}

Use the custom font

Using custom fonts is extremely simple.

Instead of providing a Font object, directly reference the custom font table like this:

textPlus.Create(
	frame,
	"Text",
	{
		Font = textPlus.CustomFonts.MyFont -- Don't require the fonts module!
	}
)


:bell: Don’t forget to stay up-to-date!

It’s highly recommended to have the latest version at all times. This ensures:

  • Newest features.
  • Best performance.
  • Little to no bugs.

You can view all releases (versions) and what they contain at the GitHub repository.
Major updates will be posted here, in the replies section, too.

Use this post as documentation for the module.
All documentation will stay up-to-date in this post.

:loudspeaker: Share your thoughts and creations!

I’d love to see what you guys are able to do with this, so consider sharing your works!

But most importantly:

  • Report any bugs or mistakes you find for this asset and post!
  • Consider providing feedback to help me and the asset improve!



Like what you're seeing?
Check out more from me!

∙ ​ Signal+|Insanely optimized script signal

Tags

module text rendering custom fonts advanced control dialogue animation creativity features open source scripting ui gui graphical interface design quality performance optimization customization

71 Likes

im nooticing :face_with_monocle:


13 Likes

It’s the previous version of this module, posted by my alt account.
I’ll get rid of the old post.

8 Likes

Oh no, it’s fine if so, it was just unsure if it was your alt or not. Instead of completely deleting it, you should edit the original post and link this post at the very top.

7 Likes

Hell yeah! Dude that’s amazing. Have been looking to something like this for a while.

3 Likes

this seems pretty, cool im bookmarking this fs!

2 Likes

This is really cool!
Im not joking im already trying to implement this into my game lol

3 Likes

how are animations applied for this module?

1 Like

Looks nice! Would text localization still works using this module?

1 Like

based on what ive seen the power of the module comes in that every character of the string is split into its own text label. You can use one of the functions to get all the characters in the text and then loop through them to do effects on them. at least thats what ive been doing. (let me know if this isnt what your supposed to be doing)

2 Likes

You should make an example place for these to show how the module should be used.

3 Likes

Unfortunately automatic text localization won’t detect text created using Text+.

1 Like

Sounds like a great idea!

Do note that there’s already a detailed guide on how you use the module, so don’t skip that!

5 Likes

Just stumbled upon this resource, and I gotta say — this is looking very promising! I’m mainly excited for the fact that this allows me to use custom fonts for my project.

Roblox’s choice of fonts is…meh. They’re fine as is, but the limitations set in place really grinds my gears. It also doesn’t help the fact that we’ve heard no further confirmations of new fonts being added as of now.

Can’t wait to try this out~
Thanks for this resource. :tada:

1 Like

How did you do the effects and the typewriting effect?, sorry if it’s kinda obvious

edit : actually i see it’s explained, but a showcase place would be cool

1 Like

Alright. Some immediate thoughts on this resource after giving it a go:

Janky Font Sizing/Positioning

I had to adjust the size of my custom font in the CustomFonts module so that it would actually be centered inside the frame (XAlignment = Enum.TextXAlignment.Center and YAlignment = Enum.TextYAlignment.Center were applied).

I think this may be dependent on the typeface and the settings I used when creating the necessary assets for it, so I’m not sure if this is a universal thing for everyone else. Still, kind of annoying to deal with, but it’s not too major.

Before (Size = 100)
Screenshot 2025-03-04 140718

After (Size = 128)
Screenshot 2025-03-04 140753

--<< Code to display text
textPlus.Create(
	text_frame_obj,
	"Some text here",
	{
		Font = Font.new(
			"Manrope",
			Enum.FontWeight.ExtraBold,
			Enum.FontStyle.Normal
		),
		
		Size = 50,
		Color = Color3.fromRGB(255, 255, 255),
		XAlignment = Enum.TextXAlignment.Center,
		YAlignment = Enum.TextYAlignment.Center,
	}
)

Lack of Some Customization Options

Firstly, it would’ve been nice if there were customization options to include text strokes (outlines on the text), especially on custom fonts. I know it’d be somewhat complicated to implement with custom fonts, so I don’t expect much in this area, but still — it’d be nice to have.

Secondly, I had to create a separate Frame object to act as the text shadow for the main text: adjusted the positioning and ZIndex of it to display beneath the main text, then I copied and pasted the code to display the main text for the shadow text with slightly differences. It’d be nice to have all of this extra work consolidated into the same block of code used to display the main text as custom options for shadow text.


These are what’s stood out to me as I was testing this resource. If there’s anything else I discover, I’ll follow through with another reply.

Still, this resource is quite ingenious~ I intend to further test it and see if it’s a good fit for my project. :+1:

2 Likes

Thanks for the constructive criticism, this is what I’m looking for!

I think this may be dependent on the typeface and the settings I used when creating the necessary assets for it, so I’m not sure if this is a universal thing for everyone else

I’ve not experienced this. Like you say, it might be to do with your typeface — some typefaces are just “janky”, having really huge line heights etc.

If you’re able to find any further information for this, perhaps a clue that it’s not the typeface, let me know!

Firstly, it would’ve been nice if there were customization options to include text strokes (outlines on the text), especially on custom fonts. I know it’d be somewhat complicated to implement with custom fonts, so I don’t expect much in this area, but still — it’d be nice to have.

I’ll definitely add this for built-in fonts.

As for custom fonts, it will be way more advanced to add strokes. I would have to use EditableImage instances, using a lot of math to draw pixels the right places — likely having to use another open-source module just for drawing the pixels — while technically not impossible, it’s a big task, complicating the module a whole lot more than needed, not at all performant. TLDR: not worth it.


Thanks for the appreciation of this resource! I’ve put a lot of work into it, and positive comments are what keeps me going.

1 Like

:bell: Version 1.1.0

New features:

  • TextBounds calculation — obtain through TextPlus.GetTextBounds().
  • Stroke customization option — uses UIStroke instances — only works for built-in fonts.
  • Line break support — you can use multiline strings.

Changes & improvements:

  • Minor performance improvements.
  • Minor bug fixes.
  • Minor comment changes and additions.

Shouldn’t this be the complete opposite? Built-in fonts should use the default textstroke option, and custom fonts should use UIstroke?

No? Custom fonts consist of ImageLabels. You can’t add strokes to images as of now in Roblox Studio — you can, but it will simply be the border of the image container, it will not respect any alpha of the actual image.

1 Like