Hotbar System starts freezing the entire roblox player

Here’s the code:

local UserInputService = game:GetService("UserInputService")
local Items = {}
local Frames = {}
local Equipped = nil
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
 
game:GetService('StarterGui'):SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, false)
 
local NumberToWord = 
    {
        [1] = "One",
        [2] = "Two",
        [3] = "Three",
        [4] = "Four",
        [5] = "Five",
        [6] = "Six",
        [7] = "Seven",
        [8] = "Eight",
        [9] = "Nine"
    }
 
 
function Scan(Location)
    print("Scanning")
    for i, Object in pairs(Location:GetChildren()) do
        if Object:IsA("Tool") then
            table.insert(Items, Object)
        end
    end
end
 
function Update()
    for i, Frame in pairs(Frames) do
        Frame:Destroy()
    end
    
    for i, Item in pairs(Items) do
        local Sample = script.Slot:Clone()
        Sample.Name = Item.Name
        Sample.LayoutOrder = i
        Sample.Parent = script.Parent.Hotbar
        Sample.Text = Item.Name
        table.insert(Frames, Sample)
        
        if Equipped ~= nil or Equipped == Item then
            Sample.BackgroundColor3 = Color3.fromRGB(140, 140, 140)
        end
        
        UserInputService.InputBegan:Connect(function(Input, Processed)
 
            if not Processed then
 
                if Input.KeyCode == Enum.KeyCode[NumberToWord[i]] then
                    if Equipped == nil or Equipped ~= Item then
                        Equipped = Item
                        Character.Humanoid:UnequipTools()
                        Character.Humanoid:EquipTool(Item)
 
                    else
                        Character.Humanoid:UnequipTools()
                        Equipped = nil
                    end
                end
            end
        end)
        
        Sample.Activated:Connect(function()
            if Equipped == nil or Equipped ~= Item then
                Equipped = Item
                Character.Humanoid:UnequipTools()
                Character.Humanoid:EquipTool(Item)
                
            else
                Character.Humanoid:UnequipTools()
                Equipped = nil
            end
        end)
    end
end
 
function BackpackChanged()
    Items = {}
    Scan(Character)
    Scan(Player.Backpack)
    Update()
end
 
Player.Backpack.ChildAdded:Connect(BackpackChanged)
Player.Backpack.ChildRemoved:Connect(BackpackChanged)
 
Character.ChildAdded:Connect(BackpackChanged)
Character.ChildRemoved:Connect(BackpackChanged)
 
BackpackChanged()

I suspect the issue is in this piece of code:

 UserInputService.InputBegan:Connect(function(Input, Processed)
 
            if not Processed then
 
                if Input.KeyCode == Enum.KeyCode[NumberToWord[i]] then
                    if Equipped == nil or Equipped ~= Item then
                        Equipped = Item
                        Character.Humanoid:UnequipTools()
                        Character.Humanoid:EquipTool(Item)
 
                    else
                        Character.Humanoid:UnequipTools()
                        Equipped = nil
                    end
                end
            end
        end)

If I click normally with the activated event it all works well but if I use the userinputservice keycode part (using hotkeys to go directly) for some reason the client starts freezing completely and the “scanning” in the scan function fires a lot of times. The amount of times “scanning” is exponential, like it’s elevated to 2 from the last time that it should be fired.

Here’s the place file to test it:
Bugged Hotbar System.rbxl (34.5 KB)

From only looking at the code you posted, it seems like your equipping is causing you a feedback loop if I had to give it a guess.

Every time you equip an item, an item is removed from the players backpack, calling BackpackChanged. Then the Update function unequips tools once Sample is activated as well as every time you press a number key (1-9), causing it to add/remove items from the players backpack which will trigger the Update function a bunch of times.

Also, every time you call Update, it’s leaving those InputBegan and Activated connections to build up in the background, exponentially adding to how many times its being processed. You should make a function or loop to disconnect those connections before calling Update. You can make a table outside of the Update function with your other variables to hold all of your connections, then run a loop to parse that table disconnecting every connection before calling the Update function.

For Example you could do

local connections = {}
local con1 = UserInputService.InputBegan:Connect(function(Input, Processed)
		--DoStuffHere
	end)
local con2 = Sample.Activated:Connect(function()
		--DoStuffHere
	end)

table.insert(connections, con1); table.insert(connections, con2)
function ClearConnections()
	for i,v in pairs (connections) do
		v:Disconnect()
	end
	connections = {}
end

function BackpackChanged()
	Items = {}
	Scan(Character)
	Scan(Player.Backpack)
	ClearConnections()
	Update()
end