Frame rate dips to 0 for one second when using InvokeServer

So I’m using the server script to get frames from my Node server (And it works good enought) and then the client makes a request to the server and grab from it the frames. However, When I don’t use jsondecode on each frame the frame rate dips to 0. If I do jsonDecode on each frame, the framerate is constantly 25 or less but there are no dips. Not sure what may cause this since jsonDecode and Encode makes arrays to a string even bigger (bits wise).

There is the server code:

local HttpService = game:GetService("HttpService")
local ReplicatedService = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local MainRemote = ReplicatedService:FindFirstChild("MainRemote")
local MainFuncion = ReplicatedService:FindFirstChild("MainFunction")
local Compression = require(ReplicatedService:WaitForChild("Compression"))

local ip = "http://redacted:3000"
local ytid = "TRYwqGjakMs"

local function RequestTime(id,Start, End)
	local Result = HttpService:RequestAsync({Url = ip.."/generate_video/"..id,Method = "GET"})
	if Result and Result.StatusCode < 300 then
		return Result.Body
	else 
		print(Result)
	end
end

local function RequestVideo(id,Start, End)
	local Result = HttpService:RequestAsync({Url = ip.."/video/"..id.."?start="..Start.."&end="..End,Method = "GET"})
	
	if Result and Result.StatusCode < 300 then
		return HttpService:JSONDecode(Result.Body)
	else 
		return {}
	end
end

local function RequestYoutube(id)
	local Result = HttpService:RequestAsync({Url = ip.."/youtube/"..id,Method = "GET"})
	if Result and Result.StatusCode < 300 then
		return HttpService:JSONDecode(Result.Body)
	else
		print(Result)
	end
end

local function RequestInfo(id)
	local Result = HttpService:RequestAsync({Url = ip.."/info/"..id,Method = "GET"})
	if Result and Result.StatusCode < 300 then
		return HttpService:JSONDecode(Result.Body)
	else
		print(Result)
	end
end

local Info = RequestInfo("server")

local Frames = {}
local Time = 0
local Increment = 0.3

RequestYoutube(ytid)
RequestTime(ytid)

coroutine.wrap(function()
	while task.wait() do
		local newBuffer = RequestVideo(ytid,Time,Time + Increment)
		Time = Time + Increment

		if #newBuffer <= 0 then
			break
		else 
			for i,v in ipairs(newBuffer) do
				table.insert(Frames,v)
			end
		end
	end
end)()

MainFuncion.OnServerInvoke = function(Client, Type, Args)
	if Type == "info" then
		return RequestInfo(Args.id)
	elseif Type == "request" then
		return RequestYoutube(Args.id)
	elseif Type == "convert" then
		return RequestTime(ytid,Args.Start,Args.End)
	elseif Type == "video" then
		local returnFrames = {}
		for i = Args.Start * Info.fps,Args.End * Info.fps do
			local Frame = Frames[i]
			if Frame then
				table.insert(returnFrames, Frame)
			end
		end
		
		return returnFrames
		--return HttpService:JSONEncode(returnFrames)
		--return Compression.Zlib.Compress(HttpService:JSONEncode(returnFrames),{level = 0})
	end
end

This is the client script

local ReplicatedService = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")

local MainRemote = ReplicatedService:FindFirstChild("MainRemote")
local MainFunction = ReplicatedService:FindFirstChild("MainFunction")
local Compression = require(ReplicatedService:WaitForChild("Compression"))

local Info = MainFunction:InvokeServer("info", {id = "server"})
local Part = workspace:WaitForChild("UI"):WaitForChild("SurfaceGui")
local Pixels = {}

local width = tonumber(Info.width)
local height = tonumber(Info.height)

for y = 1,height do
	for x = 1,width do
		local Pixel = script.Pixel:Clone()
		Pixel.Parent = Part
		Pixel.Position = UDim2.new((1/width)*x,0,(1/height) * y,0)
		Pixel.Size = UDim2.new(1/width,0,1/height,0)
		table.insert(Pixels, Pixel)
	end
	RunService.RenderStepped:Wait()
end

local FrameBuffer = {}
local Time = 0
local Increment = 1

coroutine.wrap(function()
	while task.wait() do
		local newBuffer = --[[HttpService:JSONDecode(]]MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment})--)
		--local newBuffer = HttpService:JSONDecode(MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment}))
		--local newBuffer = HttpService:JSONDecode(Compression.Zlib.Decompress(MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment})))

		if #newBuffer <= 0 then
			break
		else 
			for i,v in ipairs(newBuffer) do
				table.insert(FrameBuffer,--[[HttpService:JSONDecode(]]v)--)
			end
			Time = Time + Increment
		end
	end
end)()

local lastFrame = tick() - 1
local FrameNum = 0

while RunService.Heartbeat:Wait() do
	if ((tick() - lastFrame) >= (1/Info.fps)) and (#FrameBuffer > 0) then
		FrameNum = FrameNum + 1
		lastFrame = tick()

		if FrameNum >= #FrameBuffer then
			FrameNum = 1
		end

		local frame = FrameBuffer[FrameNum]
		for i,colors in ipairs(HttpService:JSONDecode(FrameBuffer[FrameNum])) do
			Pixels[i].BackgroundColor3 = Color3.fromRGB(colors["r"],colors["g"],colors["b"])
		end
		
		--[[for i = 1,#Pixels do
			local colors = frame[i]
			Pixels[i].BackgroundColor3 = Color3.fromRGB(colors["r"],colors["g"],colors["b"])
		end]]
	end
end

Edit: Here is the client script that works but is laggy

local ReplicatedService = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")

local MainRemote = ReplicatedService:FindFirstChild("MainRemote")
local MainFunction = ReplicatedService:FindFirstChild("MainFunction")
local Compression = require(ReplicatedService:WaitForChild("Compression"))

local Info = MainFunction:InvokeServer("info", {id = "server"})
local Part = workspace:WaitForChild("UI"):WaitForChild("SurfaceGui")
local Pixels = {}

local width = tonumber(Info.width)
local height = tonumber(Info.height)

for y = 1,height do
	for x = 1,width do
		local Pixel = script.Pixel:Clone()
		Pixel.Parent = Part
		Pixel.Position = UDim2.new((1/width)*x,0,(1/height) * y,0)
		Pixel.Size = UDim2.new(1/width,0,1/height,0)
		table.insert(Pixels, Pixel)
	end
	RunService.RenderStepped:Wait()
end

local FrameBuffer = {}
local Time = 0
local Increment = 1

coroutine.wrap(function()
	while task.wait() do
		local newBuffer = --[[HttpService:JSONDecode(]]MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment})--)
		--local newBuffer = HttpService:JSONDecode(MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment}))
		--local newBuffer = HttpService:JSONDecode(Compression.Zlib.Decompress(MainFunction:InvokeServer("video",{Start = Time,End = Time + Increment})))

		if #newBuffer <= 0 then
			break
		else 
			for i,v in ipairs(newBuffer) do
				table.insert(FrameBuffer,--[[HttpService:JSONDecode(]]v)--)
			end
			Time = Time + Increment
		end
	end
end)()

local lastFrame = tick() - 1
local FrameNum = 0

while RunService.Heartbeat:Wait() do
	if ((tick() - lastFrame) >= (1/Info.fps)) and (#FrameBuffer > 0) then
		FrameNum = FrameNum + 1
		lastFrame = tick()

		if FrameNum >= #FrameBuffer then
			FrameNum = 1
		end

		local frame = FrameBuffer[FrameNum]
		for i,colors in ipairs(HttpService:JSONDecode(FrameBuffer[FrameNum])) do
			Pixels[i].BackgroundColor3 = Color3.fromRGB(colors["r"],colors["g"],colors["b"])
		end
		
		--[[for i = 1,#Pixels do
			local colors = frame[i]
			Pixels[i].BackgroundColor3 = Color3.fromRGB(colors["r"],colors["g"],colors["b"])
		end]]
	end
end

Not sure what I did, but I fixed it. Probably compressing it did the trick or using gzip in nodejs.