Reverse time
Key RequiredUniversal

Reverse time

Universal ScriptWhheWhheWhheWhheWhheWhheWhheWhhe 188 views 2.2K executesUpdated Aug 8, 2026
Unrated
00
Lua Script•489 lines (11098 chars)
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local Lighting = game:GetService("Lighting")



local LocalPlayer = Players.LocalPlayer
local CurrentCharacter = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()

LocalPlayer.CharacterAdded:Connect(function(character)
CurrentCharacter = character
end)

-- GUI (TOP RIGHT)
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.ResetOnSpawn = false
ScreenGui.Parent = LocalPlayer:WaitForChild("PlayerGui")

local Toggle = Instance.new("TextButton")
Toggle.Size = UDim2.new(0, 120, 0, 40)
Toggle.Position = UDim2.new(1, -130, 0, 10)
Toggle.AnchorPoint = Vector2.new(0, 0)
Toggle.Text = "Reverse: OFF"
Toggle.Parent = ScreenGui

-- State
local recording = true
local reversing = false

local recorded = {}
local properties = {}
local tracked = {}
local lastCFrames = {}
local initialBuffered = {}
local destroyConnections = {}
local timeAccumulators = {}

-- HUMANOID
local humanoidRecorded = {}
local humanoidTracked = {}
local humanoidLast = {}
local humanoidInitialBuffered = {}
local humanoidAccumulators = {}

-- EXTRA RECORDING
local visualRecorded = {}
local lightingRecorded = {}
local visualTracked = {}

-- EXISTENCE
local bornAtStart = {}
local allTrackedParts = {}

-- Replication loop timer
local replicationTimer = 0
local REPLICATION_INTERVAL = 11

-- SETTINGS
local RECORD_INTERVAL = 1 / 60

local function isValidPart(part)
if not part:IsA("BasePart") then return false end
if CurrentCharacter and part:IsDescendantOf(CurrentCharacter) then return false end
return true
end

local function isHumanoid(obj)
return obj:IsA("Humanoid")
end

local function isVisual(obj)
return obj:IsA("Decal") or obj:IsA("Texture")
end

local function cleanupPart(part)
local conn = destroyConnections[part]
if conn then
conn:Disconnect()
destroyConnections[part] = nil
end

recorded[part] = nil  
properties[part] = nil  
tracked[part] = nil  
timeAccumulators[part] = nil  
lastCFrames[part] = nil  
initialBuffered[part] = nil  
bornAtStart[part] = nil

end

local function trackPart(part, fromInitialScan)
if tracked[part] then return end
if not isValidPart(part) then return end

tracked[part] = true  
recorded[part] = {}  
timeAccumulators[part] = 0  
lastCFrames[part] = part.CFrame  
initialBuffered[part] = false  
bornAtStart[part] = fromInitialScan and true or false  

properties[part] = {  
	Anchored = part.Anchored,  
	Transparency = part.Transparency  
}  

destroyConnections[part] = part.Destroying:Connect(function()  
	cleanupPart(part)  
end)

end

local function trackHumanoid(h)
if humanoidTracked[h] then return end
if not isHumanoid(h) then return end

humanoidTracked[h] = true  
humanoidRecorded[h] = {}  
humanoidAccumulators[h] = 0  
humanoidLast[h] = {  
	Health = h.Health,  
	WalkSpeed = h.WalkSpeed,  
	JumpPower = h.JumpPower  
}  
humanoidInitialBuffered[h] = false

end

local function trackVisual(obj)
if visualTracked[obj] then return end
if not isVisual(obj) then return end

visualTracked[obj] = true  
visualRecorded[obj] = {}

end

-- Initial scan
for _, v in ipairs(Workspace:GetDescendants()) do
trackPart(v, true)
trackHumanoid(v)
trackVisual(v)
end

Workspace.DescendantAdded:Connect(function(v)
trackPart(v, false)
trackHumanoid(v)
trackVisual(v)
end)

Workspace.DescendantRemoving:Connect(function(obj)
if tracked[obj] then
cleanupPart(obj)
end
if humanoidTracked[obj] then
humanoidTracked[obj] = nil
humanoidRecorded[obj] = nil
humanoidAccumulators[obj] = nil
humanoidLast[obj] = nil
humanoidInitialBuffered[obj] = nil
end
if visualTracked[obj] then
visualTracked[obj] = nil
visualRecorded[obj] = nil
end
end)

local function clearFuture()
for part in pairs(recorded) do
recorded[part] = {}
initialBuffered[part] = false
end

for h in pairs(humanoidRecorded) do  
	humanoidRecorded[h] = {}  
	humanoidInitialBuffered[h] = false  
end  

for obj in pairs(visualRecorded) do  
	visualRecorded[obj] = {}  
end  

lightingRecorded = {}

end

local function resetAccumulators()
for part in pairs(timeAccumulators) do
timeAccumulators[part] = 0
end
for h in pairs(humanoidAccumulators) do
humanoidAccumulators[h] = 0
end
end

local function applySnapshot(part, data)
part.CFrame = data.CFrame
part.Color = data.Color
part.Material = data.Material
part.Transparency = data.Transparency
part.Reflectance = data.Reflectance
end

local function autoStopIfFinished()
if not reversing then return end

local hasData = false  

for _, frames in pairs(recorded) do  
	if #frames > 0 then  
		hasData = true  
		break  
	end  
end  

if not hasData then  
	for _, frames in pairs(humanoidRecorded) do  
		if #frames > 0 then  
			hasData = true  
			break  
		end  
	end  
end  

if not hasData then  
	for _, frames in pairs(visualRecorded) do  
		if #frames > 0 then  
			hasData = true  
			break  
		end  
	end  
end  

if not hasData and #lightingRecorded > 0 then  
	hasData = true  
end  

if not hasData then  
	reversing = false  
	recording = true  

	Toggle.Text = "Reverse: OFF"  
	settings().Network.IncomingReplicationLag = 0  

	for part, prop in pairs(properties) do  
		if part and part.Parent then  
			part.Anchored = prop.Anchored  
			part.Transparency = prop.Transparency  
		end  
	end  

	clearFuture()  
end

end

RunService.Heartbeat:Connect(function(dt)
local doRecord = recording
local doReverse = reversing

if doReverse then  
	replicationTimer += dt  
	if replicationTimer >= REPLICATION_INTERVAL then  
		replicationTimer = 0  
		settings().Network.IncomingReplicationLag = math.huge  
	end  
end  

-- PARTS  
for part in pairs(tracked) do  
	if not part or not part.Parent or not part:IsDescendantOf(Workspace) then  
		cleanupPart(part)  
	else  
		local acc = (timeAccumulators[part] or 0) + dt  
		local frames = recorded[part]  

		if doRecord then  
			while acc >= RECORD_INTERVAL do  
				acc -= RECORD_INTERVAL  

				local current = part.CFrame  
				local last = lastCFrames[part]  

				if not initialBuffered[part] then  
					local data = {  
						CFrame = current,  
						Color = part.Color,  
						Material = part.Material,  
						Transparency = part.Transparency,  
						Reflectance = part.Reflectance,  
						Duration = RECORD_INTERVAL  
					}  
					frames[#frames + 1] = data  
					frames[#frames + 1] = data  
					initialBuffered[part] = true  
				else  
					if current ~= last then  
						lastCFrames[part] = current  
						frames[#frames + 1] = {  
							CFrame = current,  
							Color = part.Color,  
							Material = part.Material,  
							Transparency = part.Transparency,  
							Reflectance = part.Reflectance,  
							Duration = RECORD_INTERVAL  
						}  
					else  
						if #frames > 0 then  
							frames[#frames].Duration += RECORD_INTERVAL  
						end  
					end  
				end  
			end  
		elseif doReverse then  
			while acc > 0 and #frames > 0 do  
				local lastIndex = #frames  
				local data = frames[lastIndex]  
				local duration = data.Duration or RECORD_INTERVAL  

				if acc >= duration then  
					acc -= duration  
					applySnapshot(part, data)  
					frames[lastIndex] = nil  
				else  
					applySnapshot(part, data)  
					break  
				end  
			end  

			if #frames == 0 and bornAtStart[part] == false and part and part.Parent then  
				part.Transparency = 1  
			end  
		end  

		timeAccumulators[part] = acc  
	end  
end  

-- HUMANOIDS  
for h in pairs(humanoidTracked) do  
	if h and h.Parent then  
		local acc = (humanoidAccumulators[h] or 0) + dt  
		local frames = humanoidRecorded[h]  

		if doRecord then  
			while acc >= RECORD_INTERVAL do  
				acc -= RECORD_INTERVAL  

				local current = {  
					Health = h.Health,  
					WalkSpeed = h.WalkSpeed,  
					JumpPower = h.JumpPower  
				}  

				local last = humanoidLast[h]  

				if not humanoidInitialBuffered[h] then  
					local data = {  
						Health = current.Health,  
						WalkSpeed = current.WalkSpeed,  
						JumpPower = current.JumpPower,  
						Duration = RECORD_INTERVAL  
					}  
					frames[#frames + 1] = data  
					frames[#frames + 1] = data  
					humanoidInitialBuffered[h] = true  
				else  
					if current.Health ~= last.Health  
					or current.WalkSpeed ~= last.WalkSpeed  
					or current.JumpPower ~= last.JumpPower then  

						humanoidLast[h] = current  

						frames[#frames + 1] = {  
							Health = current.Health,  
							WalkSpeed = current.WalkSpeed,  
							JumpPower = current.JumpPower,  
							Duration = RECORD_INTERVAL  
						}  
					else  
						if #frames > 0 then  
							frames[#frames].Duration += RECORD_INTERVAL  
						end  
					end  
				end  
			end  
		elseif doReverse then  
			while acc > 0 and #frames > 0 do  
				local lastIndex = #frames  
				local data = frames[lastIndex]  
				local duration = data.Duration or RECORD_INTERVAL  

				if acc >= duration then  
					acc -= duration  
					h.Health = data.Health  
					h.WalkSpeed = data.WalkSpeed  
					h.JumpPower = data.JumpPower  
					frames[lastIndex] = nil  
				else  
					h.Health = data.Health  
					h.WalkSpeed = data.WalkSpeed  
					h.JumpPower = data.JumpPower  
					break  
				end  
			end  
		end  

		humanoidAccumulators[h] = acc  
	end  
end  

-- VISUAL  
for obj in pairs(visualTracked) do  
	local frames = visualRecorded[obj]  
	if obj and obj.Parent then  
		if doRecord then  
			frames[#frames + 1] = {  
				Texture = obj.Texture,  
				Transparency = obj.Transparency  
			}  
		elseif doReverse and #frames > 0 then  
			local data = frames[#frames]  
			obj.Texture = data.Texture  
			obj.Transparency = data.Transparency  
			frames[#frames] = nil  
		end  
	end  
end  

-- LIGHTING  
if doRecord then  
	lightingRecorded[#lightingRecorded + 1] = {  
		Brightness = Lighting.Brightness,  
		ClockTime = Lighting.ClockTime,  
		FogEnd = Lighting.FogEnd,  
		FogStart = Lighting.FogStart,  
		Ambient = Lighting.Ambient,  
		OutdoorAmbient = Lighting.OutdoorAmbient  
	}  
elseif doReverse and #lightingRecorded > 0 then  
	local data = lightingRecorded[#lightingRecorded]  
	Lighting.Brightness = data.Brightness  
	Lighting.ClockTime = data.ClockTime  
	Lighting.FogEnd = data.FogEnd  
	Lighting.FogStart = data.FogStart  
	Lighting.Ambient = data.Ambient  
	Lighting.OutdoorAmbient = data.OutdoorAmbient  
	lightingRecorded[#lightingRecorded] = nil  
end  

autoStopIfFinished()

end)

Toggle.MouseButton1Click:Connect(function()
reversing = not reversing
recording = not reversing

resetAccumulators()  
replicationTimer = 0  

if reversing then  
	Toggle.Text = "Reverse: ON"  
	settings().Network.IncomingReplicationLag = math.huge  

	for part in pairs(properties) do  
		if part and part.Parent then  
			part.Anchored = true  
		end  
	end  
else  
	Toggle.Text = "Reverse: OFF"  
	settings().Network.IncomingReplicationLag = 0  

	for part, prop in pairs(properties) do  
		if part and part.Parent then  
			part.Anchored = prop.Anchored  
			part.Transparency = prop.Transparency  
		end  
	end  

	clearFuture()  
end

end)

Workspace.FallenPartsDestroyHeight = -math.huge

Features & Information

Reverse movement of the world, does NOT repair anything, it's CLIENT side, but if you have a OWNERSHIP of a part, it DOES server replicated, does NOT reverse particles, does NOT reverse gui, does NOT affect local player, it only reverse cframes of parts, low end devices are NOT supported (might lag alot).
#Roblox#Rewind time#Client side

Community Comments (0)

No comments yet for this script.

More Scripts for Universal Scripts