Multi-threading help

When i use this script only 1/(the number of workers) of this screen is rendered e.g.

local numWorkers = 2
makes it so that only 1/2 of the screen is rendered
does anyone know how to fix this?

--!native
local ScreenGui = script.Parent

if ScreenGui:IsA("Actor") then
	ScreenGui = ScreenGui.Parent.Parent
end

local Canvas = ScreenGui:WaitForChild("Frame")
local RunService = game:GetService("RunService")

local pixelSize = 12

local pixels = {}
local canvasWidth, canvasHeight

local function initializePixels()
	canvasWidth = math.floor(Canvas.AbsoluteSize.X / pixelSize)
	canvasHeight = math.floor(Canvas.AbsoluteSize.Y / pixelSize)

	for _, pixelRow in pairs(Canvas:GetChildren()) do
		pixelRow:Destroy()
	end

	pixels = {}

	for y = 0, canvasHeight - 1 do
		pixels[y] = {}
		for x = 0, canvasWidth - 1 do
			local pixel = Instance.new("Frame")
			pixel.Size = UDim2.new(0, pixelSize, 0, pixelSize)
			pixel.Position = UDim2.new(0, x * pixelSize, 0, y * pixelSize)
			pixel.BorderSizePixel = 0
			pixel.BackgroundColor3 = Color3.new(0, 0, 0)
			pixel.Parent = Canvas
			pixels[y][x] = {
				frame = pixel,
				color = Color3.new(0, 0, 0)
			}
		end
	end
end

local function getColor(hit)
	if hit then
		return hit.Instance.BrickColor.Color
	else
		return Color3.new(0, 0, 0)
	end
end

local function raytrace(camera, x, y, aspectRatio, fov)
	local screenX = (x / canvasWidth) * 2 - 1
	local screenY = (y / canvasHeight) * 2 - 1
	screenX = screenX * aspectRatio * math.tan(fov / 2)
	screenY = screenY * math.tan(fov / 2)
	local rayDirection = (camera.CFrame.LookVector + camera.CFrame.RightVector * screenX - camera.CFrame.UpVector * screenY).Unit
	local ray = Ray.new(camera.CFrame.Position, rayDirection * 100)
	local hit = game.Workspace:Raycast(camera.CFrame.Position, rayDirection * 100)

	return getColor(hit)
end

local function updatePixels(startY, endY)
	local camera = game.Workspace.CurrentCamera
	local aspectRatio = canvasWidth / canvasHeight
	local fov = math.rad(90)

	for y = startY, endY do
		for x = 0, canvasWidth - 1 do
			local color = raytrace(camera, x, y, aspectRatio, fov)
			if color ~= pixels[y][x].color then
				pixels[y][x].color = color
				task.synchronize()
				pixels[y][x].frame.BackgroundColor3 = color
				task.desynchronize()
			end
		end
	end
end

initializePixels()

local actor = script:GetActor()
if actor == nil then
	local workers = {}
	local numWorkers = 2
	for i = 1, numWorkers do
		local actor = Instance.new("Actor")
		script:Clone().Parent = actor
		table.insert(workers, actor)
	end

	for _, actor in ipairs(workers) do
		actor.Parent = script
	end

	while true do
		task.wait()
		for i, worker in ipairs(workers) do
			local startY = (i - 1) * math.ceil(canvasHeight / numWorkers)
			local endY = math.min(startY + math.ceil(canvasHeight / numWorkers) - 1, canvasHeight - 1)
			print(startY, endY)
			worker:SendMessage("Raycast", startY, endY)
		end
	end

	return
end

actor:BindToMessageParallel("Raycast", function(startY, endY)
	updatePixels(startY, endY)
end)

Canvas:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
	initializePixels()
end)

Try this code, and unparent your main script from the Actor you’ve got it parented to (this is the only reason it wouldn’t be working according to this code)

--!native
local ScreenGui = script.Parent

if ScreenGui:IsA("Actor") then
	ScreenGui = ScreenGui.Parent.Parent
end

local Canvas = ScreenGui:WaitForChild("Frame")
local RunService = game:GetService("RunService")

local pixelSize = 12

local pixels = {}
local canvasWidth, canvasHeight

local function initializePixels()
	canvasWidth = math.floor(Canvas.AbsoluteSize.X / pixelSize)
	canvasHeight = math.floor(Canvas.AbsoluteSize.Y / pixelSize)

	for _, pixelRow in pairs(Canvas:GetChildren()) do
		pixelRow:Destroy()
	end

	pixels = {}

	for y = 0, canvasHeight - 1 do
		pixels[y] = {}
		for x = 0, canvasWidth - 1 do
			local pixel = Instance.new("Frame")
			pixel.Size = UDim2.new(0, pixelSize, 0, pixelSize)
			pixel.Position = UDim2.new(0, x * pixelSize, 0, y * pixelSize)
			pixel.BorderSizePixel = 0
			pixel.BackgroundColor3 = Color3.new(0, 0, 0)
			pixel.Parent = Canvas
			pixels[y][x] = {
				frame = pixel,
				color = Color3.new(0, 0, 0)
			}
		end
	end
end

local function getColor(hit)
	if hit then
		return hit.Instance.BrickColor.Color
	else
		return Color3.new(0, 0, 0)
	end
end

local function raytrace(camera, x, y, aspectRatio, fov)
	local screenX = (x / canvasWidth) * 2 - 1
	local screenY = (y / canvasHeight) * 2 - 1
	screenX = screenX * aspectRatio * math.tan(fov / 2)
	screenY = screenY * math.tan(fov / 2)
	local rayDirection = (camera.CFrame.LookVector + camera.CFrame.RightVector * screenX - camera.CFrame.UpVector * screenY).Unit
	local ray = Ray.new(camera.CFrame.Position, rayDirection * 100)
	local hit = game.Workspace:Raycast(camera.CFrame.Position, rayDirection * 100)

	return getColor(hit)
end

local function updatePixels(startY, endY)
	task.desynchronize()
	local camera = game.Workspace.CurrentCamera
	local aspectRatio = canvasWidth / canvasHeight
	local fov = math.rad(90)

	for y = startY, endY do
		for x = 0, canvasWidth - 1 do
			local color = raytrace(camera, x, y, aspectRatio, fov)
			if color ~= pixels[y][x].color then
				pixels[y][x].color = color
			end
		end
	end
	
	task.synchronize()
	
	for y = startY, endY do -- We don't want to make multithreading useless, so we don't update properties and resynchronize until we've done all the calculations (raycasting)
		for x = 0, canvasWidth - 1 do
			task.synchronize()
			pixels[y][x].frame.BackgroundColor3 = pixels[y][x].color
			task.desynchronize()
		end
	end
	
end

-- !!!!!!! Do not parent the main script to an actor. It will not work !!!!!!!!!!
local actor = script:GetActor()

-- This code checks if the current script is a child of an actor and does not set up the listener for the main script to call nor does it create any new scripts/threads!
if actor == nil then
	
	local workers = {}
	
	initializePixels() -- We only want to create the pixels in one script.
	
	Canvas:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		initializePixels()
		for i, worker in ipairs(workers) do
			worker:SendMessage("PixelsUpdated", pixels)
		end
	end)
	
	local numWorkers = 2
	for i = 1, numWorkers do
		local actor = Instance.new("Actor")
		actor:SetAttribute("Parent", false)
		actor:SetAttribute("Child", true)
		script:Clone().Parent = actor
		table.insert(workers, actor)
	end

	for _, actor in ipairs(workers) do
		actor.Parent = script
	end
	
	for i, worker in ipairs(workers) do
		worker:SendMessage("PixelsUpdated", pixels)
	end

	while true do
		task.wait()
		for i, worker in ipairs(workers) do
			local startY = (i - 1) * math.ceil(canvasHeight / numWorkers)
			local endY = math.min(startY + math.ceil(canvasHeight / numWorkers) - 1, canvasHeight - 1)
			print(startY, endY)
			worker:SendMessage("Raycast", startY, endY)
		end
	end

	return
end

actor:BindToMessageParallel("PixelsUpdated", function(PixelData)
	pixels = PixelData
end)

actor:BindToMessageParallel("Raycast", function(startY, endY)
	updatePixels(startY, endY)
end)

Players.NutHead_Jeff.PlayerGui.Canvas.LocalScript.Actor.LocalScript:66: attempt to perform arithmetic (div) on nil - Client - LocalScript:66 i got this error, i could fix it but u fix it for me

--!native
--!optimize 2
local Framerate = 60 -- this is in Frames per second, it is a hard limit for the code.
local ScreenGui -- This was causing the issue.

if script.Parent:IsA("Actor") and script.Parent.Parent:IsA("ScreenGui") then
	ScreenGui = script.Parent.Parent
elseif script.Parent:IsA("Actor") and not script.Parent.Parent:IsA("ScreenGui") then
	error("Something is very wrong with the actors parenting code, check the bottom and ensure it's parenting actors to the screengui.")
elseif script.Parent:IsA("ScreenGui") then
	ScreenGui = script.Parent
end

local Canvas = ScreenGui:WaitForChild("Frame")
local RunService = game:GetService("RunService")

local pixelSize = 12

local pixels = {}
local canvasWidth, canvasHeight

local FPSDelta = 1/Framerate

local function initializePixels()
	if script.Parent:IsA("Actor") then return end
	canvasWidth = math.floor(Canvas.AbsoluteSize.X / pixelSize)
	canvasHeight = math.floor(Canvas.AbsoluteSize.Y / pixelSize)

	for _, pixelRow in pairs(Canvas:GetChildren()) do
		pixelRow:Destroy()
	end

	pixels = {}

	for y = 0, canvasHeight - 1 do
		pixels[y] = {}
		for x = 0, canvasWidth - 1 do
			local pixel = Instance.new("Frame")
			pixel.Size = UDim2.new(0, pixelSize, 0, pixelSize)
			pixel.Position = UDim2.new(0, x * pixelSize, 0, y * pixelSize)
			pixel.BorderSizePixel = 0
			pixel.BackgroundColor3 = Color3.new(0, 0, 0)
			pixel.Parent = Canvas
			pixels[y][x] = {
				frame = pixel,
				color = Color3.new(0, 0, 0)
			}
		end
	end
end

local function getColor(hit)
	if hit then
		return hit.Instance.BrickColor.Color
	else
		return Color3.new(0, 0, 0)
	end
end

local function raytrace(camera, x, y, aspectRatio, fov)
	local screenX = (x / canvasWidth) * 2 - 1
	local screenY = (y / canvasHeight) * 2 - 1
	screenX = screenX * aspectRatio * math.tan(fov / 2)
	screenY = screenY * math.tan(fov / 2)
	local rayDirection = (camera.CFrame.LookVector + camera.CFrame.RightVector * screenX - camera.CFrame.UpVector * screenY).Unit
	local ray = Ray.new(camera.CFrame.Position, rayDirection * 100)
	local hit = game.Workspace:Raycast(camera.CFrame.Position, rayDirection * 100)

	return getColor(hit)
end

local function updatePixels(startY, endY)
	task.desynchronize()
	local camera = game.Workspace.CurrentCamera
	local aspectRatio = canvasWidth / canvasHeight
	local fov = math.rad(90)

	for y = startY, endY do
		for x = 0, canvasWidth - 1 do
			local color = raytrace(camera, x, y, aspectRatio, fov)
			if color ~= pixels[y][x].color then
				pixels[y][x].color = color
			end
		end
	end
	
	task.synchronize()
	
	for y = startY, endY do -- We don't want to make multithreading useless, so we don't update properties and resynchronize until we've done all the calculations (raycasting)
		for x = 0, canvasWidth - 1 do
			pixels[y][x].frame.BackgroundColor3 = pixels[y][x].color
		end
	end
	
end

-- !!!!!!! Do not parent the main script to an actor. It will not work !!!!!!!!!!
local actor = script:GetActor()

-- This code checks if the current script is a child of an actor and does not set up the listener for the main script to call nor does it create any new scripts/threads!
if actor == nil then
	
	local workers = {}
	
	initializePixels() -- We only want to create the pixels in one script.
	
	Canvas:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
		initializePixels()
		for i, worker in ipairs(workers) do
			worker:SendMessage("PixelsUpdated", pixels, canvasWidth, canvasHeight)
		end
	end)
	
	local numWorkers = 2
	for i = 1, numWorkers do
		local actor = Instance.new("Actor")
		actor:SetAttribute("Parent", false)
		actor:SetAttribute("Child", true)
		script:Clone().Parent = actor
		table.insert(workers, actor)
	end

	for _, actor in ipairs(workers) do
		actor.Parent = script.Parent
	end
	
	for i, worker in ipairs(workers) do
		worker:SendMessage("PixelsUpdated", pixels, canvasWidth, canvasHeight)
	end

	while true do
		task.wait(FPSDelta) -- FPS is now adjustable at the top, input the actual frames per second you want.
		for i, worker in ipairs(workers) do
			local startY = (i - 1) * math.ceil(canvasHeight / numWorkers)
			local endY = math.min(startY + math.ceil(canvasHeight / numWorkers) - 1, canvasHeight - 1)
			worker:SendMessage("Raycast", startY, endY)
		end
	end

	return
end

actor:BindToMessageParallel("PixelsUpdated", function(...)
	pixels, canvasWidth, canvasHeight = ...
end)

actor:BindToMessageParallel("Raycast", function(startY, endY)
	updatePixels(startY, endY)
end)