Drawing Gui Optimisation Help

hello to anyone who’s seeing this. i’ve made a drawing gui in my roblox game however after a bit of usage it causes the game to lag A LOT. i’m pretty sure this is due to the excessive cloning and assets this scrip causes but i don’t know how to optimise/fix this.

please help!!

game for reference: doodl_io - Roblox

-- add max per layer

local userInput = game:GetService("UserInputService")

local isMouseDown = false -- Track the mouse state

local UserInputService = game:GetService("UserInputService")
local frame = script.Parent -- Replace with your target GUI object
local brushTemplate = script.Parent.Brush -- Reference to the brush template

local previousPosition = nil -- Store the previous position for interpolation

-- Detect when the mouse button is pressed
userInput.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end -- Ignore inputs when UI or other game features consume them

	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		isMouseDown = true
		script.Parent.Held.Value = true

	end
end)

-- Detect when the mouse button is released
userInput.InputEnded:Connect(function(input, gameProcessed)
	if gameProcessed then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
		isMouseDown = false
		script.Parent.Held.Value = false
		previousPosition = nil
	end
end)

-- Example: Check mouse state continuously
game:GetService("RunService").RenderStepped:Connect(function()
	if isMouseDown then
		script.Parent.Held.Value = true
	else
		script.Parent.Held.Value = false
		previousPosition = nil
	end
end)
game:GetService("RunService").RenderStepped:Connect(function()
	-- Drawing logic here
end)

UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then

		local mouse = game.Players.LocalPlayer:GetMouse()
		local ContainerPosition = script.Parent.AbsolutePosition
		script.Parent.BrushStroke.Position = UDim2.new((mouse.X - ContainerPosition.X)/script.Parent.AbsoluteSize.X, 0, (mouse.Y - ContainerPosition.Y)/script.Parent.AbsoluteSize.Y, 0)

		if script.Parent.Held.Value == true then
			local mousePosition = UserInputService:GetMouseLocation()
			local containerPosition = frame.AbsolutePosition
			local containerSize = frame.AbsoluteSize

			-- Calculate the current position in UDim2
			local currentPosition = UDim2.new(
				(mouse.X - containerPosition.X) / containerSize.X,
				0,
				(mouse.Y - containerPosition.Y) / containerSize.Y,
				0
			)

			-- If there was a previous position, interpolate between it and the current position
			if previousPosition then
				local start = Vector2.new(
					previousPosition.X.Scale * containerSize.X,
					previousPosition.Y.Scale * containerSize.Y
				)
				local finish = Vector2.new(
					currentPosition.X.Scale * containerSize.X,
					currentPosition.Y.Scale * containerSize.Y
				)
				local distance = (finish - start).Magnitude
				local step = math.max(brushTemplate.AbsoluteSize.X / 5, distance / 100) -- Number of pixels between brush strokes

				-- Create interpolated brush strokes
				for i = 0, distance, step do
					local t = i / distance
					local interpolatedPosition = start:Lerp(finish, t)

					-- Convert interpolated position back to UDim2
					local interpolatedUDim2 = UDim2.new(
						interpolatedPosition.X / containerSize.X,
						0,
						interpolatedPosition.Y / containerSize.Y,
						0
					)

					if interpolatedUDim2.X.Scale < 1 or interpolatedUDim2.Y.Scale < 1 and interpolatedUDim2.X.Scale >= 0 or interpolatedUDim2.Y.Scale >= 0 then
						-- Create and position a brush clone
						local clone = brushTemplate:Clone()
						clone.Position = interpolatedUDim2
						clone.Parent = frame
					end					
				end
			end

			if currentPosition.X.Scale < 1 and currentPosition.Y.Scale < 1 and currentPosition.X.Scale >= 0 and currentPosition.Y.Scale >= 0 then
				-- Update the brush stroke and set the previous position
				local clone = brushTemplate:Clone()
				clone.Position = currentPosition
				clone.Parent = frame
				previousPosition = currentPosition
			end	

			script.Parent.BrushStroke.Visible = true

			-- Store for the next frame
		else
			previousPosition = nil
		end
	end
end)

UserInputService.TouchMoved:Connect(function(touch)
	print("touch moved")
	local touchPosition = Vector2.new(touch.Position.X, touch.Position.Y)
	local ContainerPosition = script.Parent.AbsolutePosition
	script.Parent.BrushStroke.Position = UDim2.new((touchPosition.X - ContainerPosition.X)/script.Parent.AbsoluteSize.X, 0, (touchPosition.Y - ContainerPosition.Y)/script.Parent.AbsoluteSize.Y, 0)

	if script.Parent.Held.Value == true then
		local containerPosition = frame.AbsolutePosition
		local containerSize = frame.AbsoluteSize

		-- Calculate the current position in UDim2
		local currentPosition = UDim2.new(
			(touchPosition.X - containerPosition.X) / containerSize.X,
			0,
			(touchPosition.Y - containerPosition.Y) / containerSize.Y,
			0
		)

		-- If there was a previous position, interpolate between it and the current position
		if previousPosition then
			local start = Vector2.new(
				previousPosition.X.Scale * containerSize.X,
				previousPosition.Y.Scale * containerSize.Y
			)
			local finish = Vector2.new(
				currentPosition.X.Scale * containerSize.X,
				currentPosition.Y.Scale * containerSize.Y
			)
			local distance = (finish - start).Magnitude
			local step =  math.max(brushTemplate.AbsoluteSize.X / 5, distance / 100) -- Number of pixels between brush strokes

			-- Create interpolated brush strokes
			for i = 0, distance, step do
				local t = i / distance
				local interpolatedPosition = start:Lerp(finish, t)

				-- Convert interpolated position back to UDim2
				local interpolatedUDim2 = UDim2.new(
					interpolatedPosition.X / containerSize.X,
					0,
					interpolatedPosition.Y / containerSize.Y,
					0
				)

				if interpolatedUDim2.X.Scale < 1 or interpolatedUDim2.Y.Scale < 1 and interpolatedUDim2.X.Scale >= 0 or interpolatedUDim2.Y.Scale >= 0 then
					-- Create and position a brush clone
					local clone = brushTemplate:Clone()
					clone.Position = interpolatedUDim2
					clone.Parent = frame
				end					
			end
		end

		if currentPosition.X.Scale < 1 and currentPosition.Y.Scale < 1 and currentPosition.X.Scale >= 0 and currentPosition.Y.Scale >= 0 then
			-- Update the brush stroke and set the previous position
			local clone = brushTemplate:Clone()
			clone.Position = currentPosition
			clone.Parent = frame
			previousPosition = currentPosition
		end	

		script.Parent.BrushStroke.Visible = true
	else
		previousPosition = nil
	end
end)

--[[UserInputService.InputChanged:Connect(function(input)
	print("Input Type:", input.UserInputType, "Position:", input.Position)
end)]]

--[[
		local mouse = game.Players.LocalPlayer:GetMouse()
		local ContainerPosition = script.Parent.AbsolutePosition
		script.Parent.BrushStroke.Position = UDim2.new((mouse.X - ContainerPosition.X)/script.Parent.AbsoluteSize.X, 0, (mouse.Y - ContainerPosition.Y)/script.Parent.AbsoluteSize.Y, 0)

		if script.Parent.Held.Value == true then
			local mousePosition = UserInputService:GetMouseLocation()
			local containerPosition = frame.AbsolutePosition
			local containerSize = frame.AbsoluteSize

			-- Calculate the current position in UDim2
			local currentPosition = UDim2.new(
				(mouse.X - containerPosition.X) / containerSize.X,
				0,
				(mouse.Y - containerPosition.Y) / containerSize.Y,
				0
			)

			-- If there was a previous position, interpolate between it and the current position
			if previousPosition then
				local start = Vector2.new(
					previousPosition.X.Scale * containerSize.X,
					previousPosition.Y.Scale * containerSize.Y
				)
				local finish = Vector2.new(
					currentPosition.X.Scale * containerSize.X,
					currentPosition.Y.Scale * containerSize.Y
				)
				local distance = (finish - start).Magnitude
				local step = math.max(brushTemplate.AbsoluteSize.X / 5, distance / 100) -- Number of pixels between brush strokes

				-- Create interpolated brush strokes
				for i = 0, distance, step do
					local t = i / distance
					local interpolatedPosition = start:Lerp(finish, t)

					-- Convert interpolated position back to UDim2
					local interpolatedUDim2 = UDim2.new(
						interpolatedPosition.X / containerSize.X,
						0,
						interpolatedPosition.Y / containerSize.Y,
						0
					)

					if interpolatedUDim2.X.Scale < 1 or interpolatedUDim2.Y.Scale < 1 and interpolatedUDim2.X.Scale >= 0 or interpolatedUDim2.Y.Scale >= 0 then
						-- Create and position a brush clone
						local clone = brushTemplate:Clone()
						clone.Position = interpolatedUDim2
						clone.Parent = frame
					end					
				end
			end

			if currentPosition.X.Scale < 1 and currentPosition.Y.Scale < 1 and currentPosition.X.Scale >= 0 and currentPosition.Y.Scale >= 0 then
				-- Update the brush stroke and set the previous position
				local clone = brushTemplate:Clone()
				clone.Position = currentPosition
				clone.Parent = frame
				previousPosition = currentPosition
			end	

			script.Parent.BrushStroke.Visible = true

			-- Store for the next frame
		else
			previousPosition = nil
		end]]

--[[-- Function to calculate the mouse's UDim2 position
local function getMouseUDim2()
	-- Get the mouse's absolute position
	local mousePos = UserInputService:GetMouseLocation()

	-- Get the frame's position and size
	local framePos = frame.AbsolutePosition
	local frameSize = frame.AbsoluteSize

	-- Calculate the relative offset position
	local relativeX = mousePos.X - framePos.X
	local relativeY = mousePos.Y - framePos.Y

	-- Ensure the position is within the frame
	if relativeX < 0 or relativeY < 0 or relativeX > frameSize.X or relativeY > frameSize.Y then
		return nil -- Mouse is outside the frame
	end

	-- Calculate scale (percentage)
	local scaleX = relativeX / frameSize.X
	local scaleY = relativeY / frameSize.Y
	
	-- Construct a UDim2
	return UDim2.new(scaleX, 0, scaleY, 0)
end

UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		-- Set the clone's position
		local mouseUDim2 = getMouseUDim2()
		if not mouseUDim2 then
			script.Parent.Held.Value = false
			previousPosition = nil
		end
	end
end)]]

also sorry the code is so long. i havent condesed it yet :sweat_smile: