Text rendering script optimization

this text rendering script i made that uses frames and text labels for every individual letter, but im running into an issue where the game freezes for a second when rendering long strings of text
is there a way i can optimize it?
(im not looking for someone to rewrite the entire script for me, i just want advice)

heres the code tho:

local cancelTyping = false
function renderText(text:string, typewriter:boolean)
	cancelTyping = false
	clearText()
	--TAGS
	local styles = {}
	local styleChanged = false
	--PARSING
	local escapeNext = false
	local parsingTag = false
	local tagIsClosing = false
	local parsedTag = ""
	local labelTextToSet = ""
	--UI ELEMENTS
	local currentWordTEMP = wordTEMP:Clone()
	local currentLabelTEMP = currentWordTEMP.LetterTEMP:Clone()
	local tickOffset = 0
	--TYPEWRITER
	local typewriterTypingITV = 0.02
	
	local function setLabelStyle(label)
		for _, style in pairs(styles) do
			if style.tagName == "rainbow" then
				letterSpecialEffects[label] = {
					effect = "rainbow", 
					label = label
				}
				currentLabelTEMP.Destroying:Connect(function()
					letterSpecialEffects[label] = nil
				end)
			elseif style.tagName == "shaky" then
				letterSpecialEffects[label] = {
					effect = "shaky", 
					label = label, 
					magnitude = style.tagParams.magnitude
				}
				currentLabelTEMP.Destroying:Connect(function()
					letterSpecialEffects[label] = nil
				end)
			elseif style.tagName == "wiggly" then
				letterSpecialEffects[label] = {
					effect = "wiggly", 
					label = label, 
					magnitude = style.tagParams.magnitude, 
					speed = style.tagParams.speed, 
					tickOffset = tickOffset
				}
				currentLabelTEMP.Destroying:Connect(function()
					letterSpecialEffects[label] = nil
				end)
			elseif style.tagName == "b" then
				label.Text = `<b>{label.Text}</b>`
			elseif style.tagName == "i" then
				label.Text = `<i>{label.Text}</i>`
			elseif style.tagName == "u" then
				label.Text = `<u>{label.Text}</u>`
			elseif style.tagName == "s" then
				label.Text = `<s>{label.Text}</s>`
			elseif style.tagName == "uc" or style.tagName == "uppercase" then
				label.Text = `<uc>{label.Text}</uc>`
			elseif style.tagName == "sc" or style.tagName == "smallcaps" then
				label.Text = `<uc>{label.Text}</uc>`
			elseif style.tagName == "lc" or style.tagName == "lowercase" then
				label.Text = string.lower(label.Text) --there is no rich text tag for this :(
			elseif style.tagName == "tw" then
				label:SetAttribute("TypingITV", tonumber(style.tagParams.itv) or 0.02)
			end
		end
	end
	
	local splitByLetter = string.split(text, "")
	for _, letter in pairs(splitByLetter) do
		tickOffset += 0.5
		--print(styles)
		if letter == "\\" and not escapeNext then
			escapeNext = true
		elseif letter == "[" and not escapeNext and not parsingTag then
			parsingTag = true
		elseif letter == "/" and parsingTag then
			tagIsClosing = true
		elseif letter == "]" and not escapeNext and parsingTag then
			parsingTag = false
			styleChanged = true
			if tagIsClosing then
				styles[parsedTag] = nil
				tagIsClosing = false
			else
				local style = {
					tagName = "", 
					tagParams = {}, 
					tickOffset = tickOffset
				}
				--print(parsedTag)
				local tagName, parameters = string.match(parsedTag, '^(%w+)%s*(.*)$')
				style.tagName = tagName
				if parameters then
					for name, data in string.gmatch(parameters, '(%w+)%s*=%s*"(.-)"') do
						print(name, data)
						style.tagParams[name] = data
					end
				end
				styles[tagName] = style
			end
			parsedTag = ""
		elseif parsingTag then
			parsedTag ..= letter
		elseif letter == " " and not parsingTag then
			--set word parent, visible and make new word and label
			currentWordTEMP.LetterTEMP:Destroy()
			currentWordTEMP = wordTEMP:Clone()
			currentWordTEMP.Name = "hi"
			currentWordTEMP.Parent = textFrame
			currentWordTEMP.Visible = true
			currentWordTEMP = wordTEMP:Clone()
			currentLabelTEMP = currentWordTEMP.LetterTEMP:Clone()
			--set label text n stuff
			currentLabelTEMP.Name = "label"
			currentLabelTEMP.Text = labelTextToSet
			currentLabelTEMP.Parent = currentWordTEMP
			currentLabelTEMP.Visible = true
			setLabelStyle(currentLabelTEMP)
			typewriterTypingITV = currentLabelTEMP:GetAttribute("TypingITV")
			if typewriter then
				for i=1, string.len(currentLabelTEMP.ContentText) do
					if cancelTyping then break end
					if currentLabelTEMP.Parent ~= currentWordTEMP then currentLabelTEMP.Parent = currentWordTEMP end
					currentLabelTEMP.Visible = true
					currentLabelTEMP.MaxVisibleGraphemes = i
					print(currentLabelTEMP.Parent)
					print("looping", currentLabelTEMP.Visible, currentLabelTEMP.MaxVisibleGraphemes)
					wait(typewriterTypingITV)
				end
			end
			labelTextToSet = ""
		else
			if styles["rainbow"] or styles["wiggly"] or styles["shaky"] or styleChanged then
				styleChanged = false
				currentLabelTEMP.Text = labelTextToSet
				currentLabelTEMP.Name = "label"
				currentLabelTEMP.Parent = currentWordTEMP
				setLabelStyle(currentLabelTEMP)
				typewriterTypingITV = currentLabelTEMP:GetAttribute("TypingITV")
				if typewriter then
					for i=1, string.len(currentLabelTEMP.ContentText) do
						if cancelTyping then break end
						if currentLabelTEMP.Parent ~= currentWordTEMP then currentLabelTEMP.Parent = currentWordTEMP end
						currentLabelTEMP.Visible = true
						currentLabelTEMP.MaxVisibleGraphemes = i
						print(currentLabelTEMP.Parent)
						print("looping", currentLabelTEMP.Visible, currentLabelTEMP.MaxVisibleGraphemes)
						wait(typewriterTypingITV)
					end
				end
				labelTextToSet = ""
				currentLabelTEMP = wordTEMP.LetterTEMP:Clone()
			end
			labelTextToSet ..= letter
		end
		if cancelTyping then break end
		--wait(0.01)
	end
	--last label and word
	currentWordTEMP.LetterTEMP:Destroy()
	currentWordTEMP = wordTEMP:Clone()
	currentWordTEMP.Name = "hi"
	currentWordTEMP.Parent = textFrame
	currentWordTEMP.Visible = true
	currentLabelTEMP = currentWordTEMP.LetterTEMP:Clone()
	currentLabelTEMP.Text = labelTextToSet
	currentLabelTEMP.Name = labelTextToSet
	currentLabelTEMP.Parent = currentWordTEMP
	setLabelStyle(currentLabelTEMP)
	typewriterTypingITV = currentLabelTEMP:GetAttribute("TypingITV")
	if typewriter then
		for i=1, string.len(currentLabelTEMP.ContentText) do
			if cancelTyping then break end
			if currentLabelTEMP.Parent ~= currentWordTEMP then currentLabelTEMP.Parent = currentWordTEMP end
			currentLabelTEMP.Visible = true
			currentLabelTEMP.MaxVisibleGraphemes = i
			print("looping LAST WORD", currentLabelTEMP.Visible, currentLabelTEMP.MaxVisibleGraphemes, currentLabelTEMP.Parent, currentLabelTEMP.Parent.Parent)
			wait(typewriterTypingITV)
		end
	end
	print("erm")
end

update: i rewrote the ENTIRE script, this could be a stupid way to do it but it lags less, i made it so that the letters merged if they didnt need to be individual.
tho i have a problem where the labels dont show up when theyre typed out

1 Like

bump
can anyone help me with this?

Not sure if this is what its meant to be used for since I haven’t used it myself, but maybe you could try looking into multithreading? It might help with the game freezing cus of a bunch of frames being created at once.

1 Like

use string.sub instead of split

erm what do you mean by that

fdghh gfh bv nv

for i = 1, #text do
    local letter = string.sub(text, i,  i )

should do the same thing but perform better, also delete the splitbyletter line

1 Like

thats an old trick even your dad would know…

you should use the new MaxVisibleGraphemes instead

and consider creating frames in batches, creating many at once causes… laaaaaaag

add ‘wait()’ between some operations especially in loops

what do you mean by creating frames in batches