Lighting

Show Deprecated
not creatable
service

The Lighting service controls global lighting in an experience. It includes a range of adjustable properties that you can use to change how lighting appears and interacts with other objects, as summarized in Lighting Properties.

Lighting may also contain an Atmosphere object to render realistic atmospheric effects, including particle density, haze, glare, and color. See Atmospheric Effects for details.

In addition, Lighting (along with Workspace.CurrentCamera) may contain post-processing effects such as SunRaysEffect and BlurEffect. See Post-Processing Effects for more information.

Code Samples

Day/Night Cycle

local Lighting = game:GetService("Lighting")
local TIME_SPEED = 60 -- 1 min = 1 hour
local START_TIME = 9 -- 9am
local minutesAfterMidnight = START_TIME * 60
local waitTime = 60 / TIME_SPEED
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
Lighting:SetMinutesAfterMidnight(minutesAfterMidnight)
task.wait(waitTime)
end

Summary

Properties

Methods

Events

Properties

Ambient

read parallel

The lighting hue applied to areas that are occluded from the sky, such as indoor areas.

This property defaults to 0, 0, 0 (black).

As long as the red, green and blue channels of this property do not exceed the corresponding channels in Lighting.OutdoorAmbient the change in hue will be reserved for areas occluded from the sun/moon. The effective Lighting.OutdoorAmbient value is clamped to be greater than or equal to Ambient in all channels. This means, if a channel of Ambient exceeds its corresponding Lighting.OutdoorAmbient channel then the hue will begin to apply to outdoor areas.

Note, when Lighting.GlobalShadows is disabled there is no distinction between areas occluded and areas that are not. In this case Lighting.OutdoorAmbient will be ignored and the hue from the Ambient property will be applied everywhere.

For more properties that influence the color of lighting, please see Lighting.ColorShiftBottom and Lighting.ColorShiftTop.

Brightness

read parallel

The intensity of illumination in the place.

Changing this value will influence the impact of the light source (sun or moon) on the map's lighting. When brightness is set to 0, there will be no effect due to Lighting.ColorShift_Top or Lighting.ColorShift_Bottom as the light source is having no effect. Note, it will not influence the shadows created by the Lighting.GlobalShadows property.

Whilst this property is not clamped, the effect is clamped between 0 and 2. Meaning setting Brightness to 10 will be no different to setting it to 2.

Note, Lighting.Ambient and Lighting.OutdoorAmbient can also be used to influence how bright a place appears. For example, setting Lighting.OutdoorAmbient to 255, 255, 255 will make the place appear brighter than its default value of 127, 127, 127 (as it is more white).

ClockTime

not replicated
read parallel

A numerical representation (in hours) of the current time of day used by Lighting.

Note, this property does not correspond with the actual time of day and will not change during the game unless it has been changed by a script.

For a measure of Lighting time formatted as a 24 hour string use Lighting.CurrentTime. Changing Lighting.CurrentTime or using Lighting:SetMinutesAfterMidnight() will also change this property.

Using ClockTime requires the time to be normalized:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local hours = minutesNormalised / 60
Lighting.ClockTime = hours
task.wait()
end

Using Lighting.TimeOfDay requires the time to be normalized and a string formatted:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local seconds = minutesNormalised * 60
local hours = string.format("%02.f", math.floor(seconds/3600))
local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)))
local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60))
local timeString = hours..":"..mins..":"..secs
Lighting.TimeOfDay = timeString
task.wait()
end

Using Lighting:SetMinutesAfterMidnight() requires no extra processing:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
Lighting:SetMinutesAfterMidnight(minutesAfterMidnight)
task.wait()
end

ColorShift_Bottom

read parallel

The hue represented in light reflected in the opposite surfaces to those facing the sun or moon.

The surfaces of a BasePart influenced by ColorShift_Bottom depends on the position and orientation of the BasePart relative to the sun or moon's position. Where the sun is directly overhead a BasePart, the shift in color will only apply to the bottom surface.

This effect can be increased or reduced by altering Lighting.Brightness.

ColorShift_Bottom influences the opposite surfaces to Lighting.ColorShift_Top

Note, Lighting.ColorShift_Top and ColorShift_Bottom will interact with the Lighting.Ambient and Lighting.OutdoorAmbient properties if they are greater than 0, 0, 0. Also, the influence of ColorShift_Bottom can be very hard to identify when Lighting.GlobalShadows is enabled (as it is by default).

ColorShift_Top

read parallel

The hue represented in light reflected from surfaces facing the sun or moon.

The surfaces of a BasePart influenced by ColorShift_Top depends on the position and orientation of the BasePart relative to the sun or moon's position. Where the sun is directly overhead a BasePart, the shift in color will only apply to the top surface.

This effect can be increased or reduced by altering Lighting.Brightness.

Whilst ColorShift_Top influences surfaces exposed to light, Lighting.ColorShift_Bottom influences surfaces sheltered from light.

Note, ColorShift_Top and ColorShift_Bottom will interact with the Lighting.Ambient and Lighting.OutdoorAmbient properties if they are greater than 0, 0, 0.

EnvironmentDiffuseScale

read parallel

Ambient light that is derived from the environment. The value of this property defaults to 0.

It is similar to Lighting.Ambient and Lighting.OutdoorAmbient property but it's dynamic and can change according sky and time of day. It is recommended when this property is increased, Ambient and OutdoorAmbient are decreased accordingly.

It also makes skybox appear at night.

EnvironmentSpecularScale

read parallel

Specular light derived from environment. The value of this property defaults to 0.

It will make smooth objects reflect the environment. Especially important to make metal more realistic.

ExposureCompensation

read parallel

This property determines the exposure compensation amount which applies a bias to the exposure level of the scene prior to the tonemap step. Defaults to 0.

  • A value of +1 indicates twice as much exposure and -1 means half as much exposure.
  • A value of 0 indicates no exposure compensation will be done.
  • Range: -5 to 5

This property is replicated and can be set from scripts or Studio.


local Lighting = game:GetService("Lighting")
Lighting.ExposureCompensation = 5

You can use this property to adjust the exposure amount prior to the tonemap step to show more detail either in lighter or darker areas. This is needed as we move to a HDR pipeline.

When Lighting.Technology is set to Legacy, this property has no effect.


local Lighting = game:GetService("Lighting")
-- ExposureCompensation has no effect because Lighting's Technology is Legacy
Lighting.Technology = Enum.Technology.Legacy
Lighting.ExposureCompensation = 5

FogColor

read parallel

Fog properties are hidden when Lighting contains an Atmosphere object.

A Color3 value giving the hue of Lighting fog.

How does fog work?

Fog in Roblox is displayed in a plane perpendicular to the Workspace.CurrentCamera direction. It fades between the Lighting.FogStart property where it is not visible, to the Lighting.FogEnd property where it is fully opaque. The effect of fog is it blends color with the FogColor.

At distances greater than Lighting.FogEnd, color will be determined entirely by the FogColor. However at distances between Lighting.FogStart and Lighting.FogEnd the degree to which the color is blended depends on the position.

Roblox's fog uses linear interpolation between Lighting.FogStart and Lighting.FogEnd. This means if Lighting.FogStart is 10 and Lighting.FogEnd is 20, at a distance of 15 studs the fog will be at 50%. That means the color of a pixel at 15 studs will be 50% its normal color blended with 50% of the fog color.


local Lighting = game:GetService("Lighting")
-- fog will fade between 25 and 200 studs
Lighting.FogStart = 25
Lighting.FogEnd = 200

Note, fog does not obscure the skybox.

FogEnd

read parallel

Fog properties are hidden when Lighting contains an Atmosphere object.

The depth from the Workspace.CurrentCamera, in studs, at which fog will be completely opaque.

How does fog work?

Fog in Roblox is displayed in a plane perpendicular to the Workspace.CurrentCamera look direction. It fades between the Lighting.FogStart property where it is not visible, to the FogEnd property where it is fully opaque. The effect of fog is it blends color with the Lighting.FogColor.

At distances greater than FogEnd, color will be determined entirely by the Lighting.FogColor. However at distances between Lighting.FogStart and FogEnd the degree to which the color is blended depends on the position.

Roblox's fog uses linear interpolation between Lighting.FogStart and FogEnd. This means if Lighting.FogStart is 10 and FogEnd is 20, at a distance of 15 studs the fog will be at 50%. That means the color of a pixel at 15 studs will be 50% its normal color blended with 50% of the fog color.


local Lighting = game:GetService("Lighting")
-- fog will fade between 25 and 200 studs
Lighting.FogStart = 25
Lighting.FogEnd = 200

The color of the fog can be adjusted using Lighting.FogColor.

Note, fog does not obscure the skybox.

FogStart

read parallel

Fog properties are hidden when Lighting contains an Atmosphere object.

The depth from the Workspace.CurrentCamera, in studs, at which fog begins to show.

How does fog work?

Fog in Roblox is displayed in a plane perpendicular to the Workspace.CurrentCamera look direction. It fades between the FogStart property where it is not visible, to the Lighting.FogEnd property where it is fully opaque. The effect of fog is it blends color with the Lighting.FogColor.

At distances greater than Lighting.FogEnd, color will be determined entirely by the Lighting.FogColor. However at distances between FogStart and Lighting.FogEnd the degree to which the color is blended depends on the position.

Roblox's fog uses linear interpolation between FogStart and Lighting.FogEnd. This means if FogStart is 10 and Lighting.FogEnd is 20, at a distance of 15 studs the fog will be at 50%. That means the color of a pixel at 15 studs will be 50% its normal color blended with 50% of the fog color.


local Lighting = game:GetService("Lighting")
-- fog will fade between 25 and 200 studs
Lighting.FogStart = 25
Lighting.FogEnd = 200

The color of the fog can be adjusted using Lighting.FogColor.

Note, fog does not obscure the skybox.

GeographicLatitude

read parallel

The geographic latitude, in degrees, of the scene, influencing the result of `Class.Lighting time on the position of the sun and moon.

When calculating the position of the sun, the earth's tilt is also taken into account.

Changing GeographicLatitude will alter the position of the sun at every Lighting.TimeOfDay. Developers looking to obtain the sun or moon's position should use Lighting:GetSunDirection() or Lighting:GetMoonDirection().

Code Samples

Sun direction

local Lighting = game:GetService("Lighting")
local UNIT_Z = Vector3.new(0, 0, 1)
local EARTH_TILT = math.rad(23.5) -- The Earth's tilt in radians.
local HALF_SOLAR_YEAR = 182.6282 -- Half the length of an average solar year
local function getSunDirection()
local gameTime = Lighting:GetMinutesAfterMidnight()
local geoLatitude = Lighting.GeographicLatitude
local dayTime = gameTime / 1440
local sourceAngle = 2 * math.pi * dayTime
local sunPosition = Vector3.new(math.sin(sourceAngle), -math.cos(sourceAngle), 0)
local latRad = math.rad(geoLatitude)
local sunOffset = -EARTH_TILT * math.cos(math.pi * (dayTime - HALF_SOLAR_YEAR) / HALF_SOLAR_YEAR) - latRad
local sunRotation = CFrame.fromAxisAngle(UNIT_Z:Cross(sunPosition), sunOffset)
local sunDirection = sunRotation * sunPosition
return sunDirection
end
print(getSunDirection())

GlobalShadows

read parallel

Toggles voxel-based dynamic lighting in the game

What does GlobalShadows do?

When set to true, shadows are rendered in sheltered areas depending on the position of the sun and moon. The lighting hue applied to these areas is determined by the Lighting.Ambient property. The lighting hue in all other areas is determined by the Lighting.OutdoorAmbient property.

When disabled, shadows are not drawn and no distinction is made between indoor and outdoor areas. As a result, the Lighting.Ambient property determines the lighting hue and Lighting.OutdoorAmbient will do nothing.

Shadows are calculated using a voxel system, and each lighting voxel is 4x4x4 studs. This means objects need to be larger than 4x4x4 studs to display a realistic shadow. Shadows are also recalculated when BaseParts are moving.

Note, this property is unrelated to shadows from characters which are displayed regardless of what GlobalShadows is set to.

For more information about Roblox's dynamic lighting, please see this blog post.

Toggling GlobalShadows

Developers toggling the GlobalShadows setting will notice that disabling it makes the place considerably darker. This is because when GlobalShadows is disabled Lighting.Ambient is used to calculate the lighting hue in both indoor and outdoor spaces. This darkness can be resolved by setting Lighting.Ambient to a higher value such as the default of Lighting.OutdoorAmbient.

In most cases developers are recommended to leave GlobalShadows enabled due to the superior visual appearance. See Lighting Properties guide for a comparison.

OutdoorAmbient

read parallel

The lighting hue applied to outdoor areas.

This property defaults to 127, 127, 127.

As long as the red, green and blue channels of Lighting.Ambient do not exceed the corresponding channels in this property, the hue of the lighting in outdoor areas will be determined by this property. The effective OutdoorAmbient value is clamped to be greater than or equal to Lighting.Ambient in all channels. This means, if a channel of Lighting.Ambient exceeds its corresponding OutdoorAmbient channel then the hue of Lighting.Ambient will begin to apply to outdoor areas.

Note, when Lighting.GlobalShadows is disabled there is no distinction between areas occluded from the sky and areas that are not. In this case OutdoorAmbient will be ignored and the hue from the Lighting.Ambient property will be applied everywhere.

For more properties that influence the color of lighting, please see Lighting.ColorShift_Bottom and Lighting.ColorShift_Top.

ShadowSoftness

read parallel

Controls how blurry the shadows are. The value of this property defaults to 0.2.

This property only works when Lighting.Technology mode is ShadowMap or Future and the device is capable of ShadowMap.

Technology

not scriptable
read parallel

Determines the lighting system for rendering the 3D world. Non-scriptable and only modifiable in Studio. See Enum.Technology for available options and Lighting Technology for detailed descriptions and visual effects of each option.

TimeOfDay

read parallel

A 24 hour string representation of the current time of day used by Lighting.

Note, this property does not correspond with the actual time of day and will not change during the game unless it has been changed by a script.

For a numeric measure of Lighting time use Lighting.ClockTime. Changing Lighting.ClockTime or using Lighting:SetMinutesAfterMidnight() will also change this property.

Using TimeOfDay requires the time to be normalized and a string formatted:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local seconds = minutesNormalised * 60
local hours = string.format("%02.f", math.floor(seconds/3600))
local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)))
local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60))
local timeString = hours..":"..mins..":"..secs
Lighting.TimeOfDay = timeString
task.wait()
end

Using Lighting.ClockTime requires the time to be normalized:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local hours = minutesNormalised / 60
Lighting.ClockTime = hours
task.wait()
end

Using Lighting:SetMinutesAfterMidnight() requires no extra processing:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
Lighting:SetMinutesAfterMidnight(minutesAfterMidnight)
task.wait()
end

Methods

GetMinutesAfterMidnight

Returns the number of minutes that have passed after midnight for the purposes of lighting.

This number will be nearly identical to Lighting.ClockTime multiplied by 60.

This number will not always be equal to the value given in Lighting:SetMinutesAfterMidnight() as it returns minutes after midnight in the current day.

For Lighting time formatted as a string, see Lighting.TimeOfDay.


Returns

The number of minutes after midnight.

Code Samples

Lighting:GetMinutesAfterMidnight

local Lighting = game:GetService("Lighting")
Lighting.TimeOfDay = "14:00:00"
print(Lighting:GetMinutesAfterMidnight())

GetMoonDirection

Returns a Vector3 representing the direction of the moon from the position 0, 0, 0.

Note, when the moon has 'set' and is no longer visible, the Vector3 returned by this function will continue to point towards the moon below the map.

Developers looking to change the positioning of the moon should use the Lighting.ClockTime or Lighting.GeographicLatitude properties.

A variant of this function exists for obtaining the direction of the sun, Lighting:GetSunDirection().


Returns

Vector3 representing the direction of the moon from the position 0, 0, 0.

Code Samples

Lighting:GetMoonDirection

local Lighting = game:GetService("Lighting")
Lighting.TimeOfDay = "14:00:00"
print(Lighting:GetMoonDirection())

GetMoonPhase

Returns the moon's current phase. There is no way to change the moon's phase so this will always return 0.75


Returns

GetSunDirection

Returns a Vector3 representing the direction of the sun from the position 0, 0, 0.

Note, when the sun has set and is no longer visible, the Vector3 returned by this function will continue to point towards the sun below the map.

Developers looking to change the positioning of the sun should use the Lighting.ClockTime or Lighting.GeographicLatitude properties.

A variant of this function exists for obtaining the direction of the moon, Lighting:GetMoonDirection().


Returns

Vector3 representing the direction of the sun from the position 0, 0, 0.

Code Samples

Lighting:GetSunDirection

local Lighting = game:GetService("Lighting")
Lighting.TimeOfDay = "14:00:00"
Lighting.GeographicLatitude = 41.73
print(Lighting:GetSunDirection())

SetMinutesAfterMidnight

void

Sets Lighting.TimeOfDay and Lighting.ClockTime to the given number of minutes after midnight.

How can I make a day / night script?

SetMinutesAfterMidnight allows a numerical value to be used, for example in a day/night cycle Script, without the need to convert to a string in the format required by Lighting.TimeOfDay. It also allows values greater than 24 hours to be given that correspond to times in the next day. See the code snippets below for an example.

Using Lighting.TimeOfDay requires the time to be normalized and a string formatted:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local seconds = minutesNormalised * 60
local hours = string.format("%02.f", math.floor(seconds/3600))
local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)))
local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60))
local timeString = hours..":"..mins..":"..secs
Lighting.TimeOfDay = timeString
task.wait()
end

Using Lighting.ClockTime requires the time to be normalized:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
local minutesNormalised = minutesAfterMidnight % (60 * 24)
local hours = minutesNormalised / 60
Lighting.ClockTime = hours
task.wait()
end

Using Lighting:SetMinutesAfterMidnight() requires no extra processing:


minutesAfterMidnight = 0
while true do
minutesAfterMidnight = minutesAfterMidnight + 1
Lighting:SetMinutesAfterMidnight(minutesAfterMidnight)
task.wait()
end

Parameters

minutes: number

The number of minutes after midnight.


Returns

void

Code Samples

Setting Lighting Time

local Lighting = game:GetService("Lighting")
Lighting:SetMinutesAfterMidnight(840)

Events

LightingChanged

This event fires when a Lighting property is changed or a Sky is added or removed from Lighting.

Although this event fires when most properties of Lighting are changed, developers should be aware of the few exceptions:

In cases where this behavior is not desired, the Instance.Changed event or Instance:GetPropertyChangedSignal() function can be used.

Parameters

skyChanged: bool

Code Samples

Lighting.LightingChanged

local Lighting = game:GetService("Lighting")
local function onLightingChanged(skyboxChanged)
if skyboxChanged then
print("Skybox has changed")
else
print("The skybox did not change.")
end
end
Lighting.LightingChanged:Connect(onLightingChanged)