Smooth Day/Night Cycle with Lighting Sync
A smooth full day/night cycle that runs on the server so every player sees the same time. Configurable cycle length, custom sunrise/sunset colors, and a 'time of day' attribute other systems can read.
“Add a day/night cycle to my game. I want a 10-minute full cycle. Everyone should see the same time, the lighting should shift through realistic sunrise and sunset colors, and other scripts should be able to ask 'what time is it'.”
Paste this — or any variation — into StudByStud and you’ll get the code below in seconds.
How it works
- The server runs a single Heartbeat loop that advances Lighting.ClockTime based on a CYCLE_MINUTES constant — change one number to make days longer or shorter.
- Because Lighting properties replicate from server to clients automatically, every player sees identical lighting without any custom replication.
- The script also tweens the ambient and outdoor ambient colors to match the time of day, giving you proper sunrise (warm orange) and sunset (red/purple) tints instead of a flat hue shift.
- It writes the current 'phase' (Dawn / Day / Dusk / Night) to a Workspace attribute, so any other script can simply read workspace:GetAttribute('TimePhase') instead of doing math.
- Performance is negligible — one Heartbeat callback that does a single addition and a property assignment per frame.
The generated code
One file. Copy it into Roblox Studio at the path shown.
--!strict
-- DayNightCycle.lua
local Lighting = game:GetService("Lighting")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Workspace = game:GetService("Workspace")
local CYCLE_MINUTES = 10 -- one full in-game day = 10 real minutes
local START_HOUR = 6 -- start at 6am
-- Color presets per phase. Tween between them as time advances.
local PHASES = {
Dawn = { hour = 5, ambient = Color3.fromRGB(120, 100, 90), outdoor = Color3.fromRGB(180, 150, 130) },
Day = { hour = 12, ambient = Color3.fromRGB(180, 180, 180), outdoor = Color3.fromRGB(180, 180, 180) },
Dusk = { hour = 18, ambient = Color3.fromRGB(150, 100, 110), outdoor = Color3.fromRGB(200, 120, 100) },
Night = { hour = 22, ambient = Color3.fromRGB(40, 45, 70), outdoor = Color3.fromRGB(60, 70, 100) },
}
local function getPhase(hour: number): string
if hour < 8 then return "Dawn"
elseif hour < 17 then return "Day"
elseif hour < 20 then return "Dusk"
else return "Night" end
end
local currentPhase: string? = nil
local function applyPhase(phase: string)
if phase == currentPhase then return end
currentPhase = phase
Workspace:SetAttribute("TimePhase", phase)
local preset = PHASES[phase]
if not preset then return end
local info = TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
TweenService:Create(Lighting, info, {
Ambient = preset.ambient,
OutdoorAmbient = preset.outdoor,
}):Play()
end
Lighting.ClockTime = START_HOUR
applyPhase(getPhase(START_HOUR))
-- 24 in-game hours per CYCLE_MINUTES * 60 real seconds.
-- That's (24 / (CYCLE_MINUTES * 60)) hours per real second.
local hoursPerSecond = 24 / (CYCLE_MINUTES * 60)
RunService.Heartbeat:Connect(function(dt)
local newClock = (Lighting.ClockTime + hoursPerSecond * dt) % 24
Lighting.ClockTime = newClock
applyPhase(getPhase(newClock))
end)
The complete cycle. One file. Drop it into ServerScriptService and you have a working day/night cycle.
What you’ll learn
Want this built for your game?
Sign up, paste the prompt above, and StudByStud will generate it — and sync it straight into Roblox Studio. Free tier includes 1M Flash tokens per month.
