Title system performance issues

I created a title system for my current project, but it seems very performance intensive when playing. Is there anyway I could optimize this script?

Main Title Script (LocalScript):

local GPD = game.ReplicatedStorage.GetPlayerData
local TEE = game.ReplicatedStorage.TitleEquipEvent

local plr = game.Players.LocalPlayer

local function deactivateTitles(exception)
	for _,v in pairs(script.Parent:GetChildren()) do
		if v:IsA("Frame") and v.Name ~= exception then
			local actButton = v:FindFirstChild("ActivateButton")
			actButton.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
			actButton.Text = 'OFF'
		end
	end
end

local function confirmRequirements(reqText)
	local requirement , requirementType
	local child
	for _,v in pairs(reqText:GetChildren()) do
		child = v
		break
	end
	requirement = child.value
	requirementType = child.Name
	
	return requirement , requirementType
end

local badgeService = game:GetService("BadgeService")

local function checkPlayerRequirements(requirementType , requirement)
	if requirementType == 'Badge' then
		return badgeService:UserHasBadgeAsync(plr.UserId, requirement)
	elseif requirementType == 'Wins' then
		return plr.leaderstats.Wins.Value >= requirement
	else
		local playerData = GPD:InvokeServer(requirementType) --ask server for playerdata (stored in serverstorage)
		return playerData >= requirement
	end
end

local function addTitle(title , requirementType , requirement)
	local playerData = checkPlayerRequirements(requirementType , requirement)
	if playerData then
		TEE:FireServer(true , title)
		return
	end
end

local function setOwned(owned , title) --to avoid duplicating 10+ lines of code to check when gui is opened
	
	if owned then
		title.Owned.Text = 'Owned'
		title.Owned.TextColor3 = Color3.fromRGB(0, 255, 127)
	else
		title.Owned.Text = 'Not Owned'
		title.Owned.TextColor3 = Color3.fromRGB(170, 61, 61)
	end
end

task.wait(2)

for _,v in pairs(script.Parent:GetChildren()) do
	if v:IsA("Frame") then
		local actButton = v:FindFirstChild("ActivateButton")
		
		local title = v.Name

		local requirementText = v.Requirement
		local requirement , requirementType = confirmRequirements(requirementText)
		
		local owned = checkPlayerRequirements(requirementType, requirement)
		
		setOwned(owned , v)
		
		actButton.InputBegan:Connect(function(input)
			if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
				if actButton.BackgroundColor3 == Color3.fromRGB(255, 0, 0) then
					local canAfford = checkPlayerRequirements(requirementType, requirement) --checks requirements again (might cause performance issues)
					if canAfford then
						addTitle(title, requirementType, requirement)
					else return end
					
					actButton.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
					actButton.Text = "ON"
					deactivateTitles(title)
					
					
				else
					actButton.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
					actButton.Text = 'OFF'
					TEE:FireServer(false , title)
				end
			end
		end)
	end
end

script.Parent.Parent.TitleButton.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		for _,v in pairs(script.Parent:GetChildren()) do
			if v:IsA("Frame") then
				local requirementText = v.Requirement
				local requirement , requirementType = confirmRequirements(requirementText)
				
				local owned = checkPlayerRequirements(requirementType, requirement)
				setOwned(owned , v)
			end
		end
	end
end)

Here’s the script on the other end of the server invoke. I need this because most, if not all, player data is stored in ServerStorage.

local GPD = game.ReplicatedStorage.GetPlayerData

local playerDataFolder = game.ServerStorage.PlayerData

GPD.OnServerInvoke = function(player , requestedData)
	local pd = playerDataFolder:FindFirstChild(player.Name.."_valuefolder")
	local data
	for _,v in pairs(pd:GetDescendants()) do
		if v.Name == requestedData then
			data = v.Value
			return data
		end
	end
end

Finally, this is the script that applies the title (probably not needed in this case, but I’ll show it anyway):

local TEE = game.ReplicatedStorage.TitleEquipEvent
local SCE = game.ReplicatedStorage.ServerCosmeticEvent

local TitleFolder = game.ServerStorage.Titles

local playerDataFolder = game.ServerStorage.PlayerData

local function AddTitle(player, add , title)
	local char = player.Character
	local head = char.Head
	for _,v in pairs(head:GetChildren()) do
		if v:IsA("BillboardGui") then
			v:Destroy()
		end
	end
	if add then
		local newTitle = TitleFolder:FindFirstChild(title):Clone()
		newTitle.Parent = head
		
		playerDataFolder[player.Name.."_valuefolder"].CurrentCosmetics.Title.Value = title
		TEE:FireClient(player , true , title)
	else
		TEE:FireClient(player , false , title)
		playerDataFolder[player.Name.."_valuefolder"].CurrentCosmetics.Title.Value = ''
	end
end
TEE.OnServerEvent:Connect(AddTitle)

SCE.Event:Connect(function(player , cosmeticType , cosmeticName)
	if cosmeticType == 'Title' and cosmeticName and cosmeticName ~= '' and cosmeticName ~= ' ' then
		AddTitle(player , true , cosmeticName)
	end
end)

Some screenshots of the explorer:

image

How player data is stored on the server (each folder is created when the play joins):

image

Here’s what the gui looks like:

It can get delayed by up to a second sometimes when equipping a title. It’s instant when turning it off though.

Again, I was just wondering it there was anything I could do to optimize the main script. Maybe there’s a better way to make a title system.

Thanks for your help in advance!