Need help optimizing Voronoi Noise Generator

local RunService = game:GetService("RunService")
local AssetService = game:GetService("AssetService")
local UserInputService = game:GetService("UserInputService")

local eimage = nil
eimage = AssetService:CreateEditableImage()
eimage:DrawRectangle(Vector2.zero, eimage.Size, Color3.new(1, 1, 1), 0, Enum.ImageCombineType.BlendSourceOver)

local points = 25 -- If your using the grid division optimisation, make sure to increase by the next whole sqrt
local cellCount = math.sqrt(points)
local pixelsBuffer = eimage:ReadPixelsBuffer(Vector2.zero, eimage.Size)
local n = 1
local xTile = {0, eimage.Size.X, eimage.Size.X, eimage.Size.X, 0, -eimage.Size.X, -eimage.Size.X, -eimage.Size.X}
local yTile = {eimage.Size.Y, eimage.Size.Y, 0, -eimage.Size.Y, -eimage.Size.Y, -eimage.Size.Y, 0, eimage.Size.Y}

local ImageLabelSource = workspace:WaitForChild("Canvas").SurfaceGui:GetChildren()

function map(value, fromMin, fromMax, toMin, toMax)
	return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin
end

function createNoise(Px, Py)
	for i = 1, eimage.Size.X * eimage.Size.Y do
		local x = (i - 1) % eimage.Size.X  -- Get the x-coordinate of the pixel
		local y = math.floor((i - 1) / eimage.Size.X)  -- Get the y-coordinate of the pixel
		local distances = {}

		local pixelIndex = (i - 1) * 4  -- Each pixel uses 4 bytes (RGBA)

		-- Calculate distances from (x, y) to all points
		for j = 1, points do
			local distance = math.sqrt(math.pow((Px[j] - x), 2) + math.pow((Py[j] - y), 2))
			table.insert(distances, distance)
		end

		-- Sort distances to find the nth smallest
		table.sort(distances)
		local distance = distances[n] or 0 
		local noise = map(distance, 0, eimage.Size.X / 2, 255, -200)

		-- Write noise as pixel color
		buffer.writeu8(pixelsBuffer, pixelIndex, noise)     -- Red channel
		buffer.writeu8(pixelsBuffer, pixelIndex + 1, noise) -- Green channel
		buffer.writeu8(pixelsBuffer, pixelIndex + 2, noise) -- Blue channel
	end

	-- Write to the image and display it
	eimage:WritePixelsBuffer(Vector2.zero, eimage.Size, pixelsBuffer)
end

function displayImage()
	for i, label in pairs(ImageLabelSource) do
		label.ImageContent = Content.fromObject(eimage)
	end
end

function createSpots()
	local x = {}
	local y = {}
	local xPos = 0
	local yPos = 0
	local xInc = 0
	local yInc = 0

	for yi = 1, cellCount do
		yPos += (eimage.Size.X / cellCount)
		for xi = 1, cellCount do
			xPos += (eimage.Size.X / cellCount)
			table.insert(x, math.random(xInc, xPos))
			table.insert(y, math.random(yInc, yPos))
			xInc += (eimage.Size.X / cellCount)
		end
		yInc += (eimage.Size.X / cellCount) 
		xPos = 0
		xInc = 0
	end

	-- Add tiling offsets
	for i = 1, 8 do
		for j = 1, points do
			-- Add the tile offsets to each spot
			table.insert(x, x[j] + xTile[i])
			table.insert(y, y[j] + yTile[i])
		end
	end

	return x, y
end


function drawSpots(x, y)

	for i = 1, points do
		eimage:DrawCircle(Vector2.new(x[i], y[i]), 8, Color3.new(0, 255, 0), 0, Enum.ImageCombineType.BlendSourceOver)
	end

	game.Workspace:WaitForChild("Canvas"):WaitForChild("SurfaceGui"):WaitForChild("ImageLabel").ImageContent = Content.fromObject(eimage)
	return
end

function drawGrid()
	local x = eimage.Size.X / cellCount
	local xInc = eimage.Size.X / cellCount
	for i = 1, (cellCount - 1) do
		-- Draw vertical lines
		eimage:DrawLine(Vector2.new(x, 1), Vector2.new(x, eimage.Size.Y - 1), 
			Color3.new(0, 0, 0), 0, Enum.ImageCombineType.BlendSourceOver)
		x += xInc
	end

	local y = eimage.Size.Y / cellCount
	local yInc = eimage.Size.Y / cellCount
	for i = 1, (cellCount - 1) do
		-- Draw horizontal lines
		eimage:DrawLine(Vector2.new(1, y), Vector2.new(eimage.Size.X - 1, y), 
			Color3.new(0, 0, 0), 0, Enum.ImageCombineType.BlendSourceOver)
		y += yInc
	end
end

UserInputService.InputBegan:Connect(function(input, gameProcessed)
	if gameProcessed then return end  -- Ignore input if it is already processed (e.g., in a GUI)
	if input.KeyCode == Enum.KeyCode.F then
		local x, y = createSpots()
		createNoise(x, y)  -- Call createCanvas when F is pressed
		displayImage()
		drawSpots(x, y)
		drawGrid()
	end
end)

Video of it in action:

Basically for createNoise(Px, Py), it calculates and stores the distance of each pixel to each point, which I have 25 points so it lags every time I render out a frame. I technique I saw was to split the canvas into an even grid and assign each grid square to a point.

I already managed to create that in createSpots() but I don’t know how to actually implement the optimization, that is checking every adjacent square of the pixel you desire. Basically, for each pixel, 100% of the time the closest point will either be in the direct square or 8 of the adjacent squares surrounding the initial square you pick. That way instead of looping through 25 points, you only need to loop through 9.

How do I implement this?

Thanks