Skip to main content

Spotlight

This item only works when running on the client. Client

Dims the screen and cuts a hole around a GuiObject.

You normally reach this through FocusUI, which drives it for you. Use it directly only when you want the highlight without a step around it.

const spotlight = TutorialKit.Spotlight.create({ DimTransparency = 0.5 })

spotlight:Focus(shopButton)
task.wait(3)
spotlight:Clear()

Why the panels live in a CanvasGroup

The obvious build for "dim everything except this rectangle" is four semi-transparent frames around a hole. Their alpha stacks wherever they touch, and that overlap renders as a visible seam along the edge of the hole. Padding cannot fix it, because the problem is overlap, not spacing.

A CanvasGroup flattens its descendants into a single image and applies GroupTransparency to the result, so the panels can be fully opaque and overlap freely without ever double-darkening a pixel.

The highlight stroke and the pointer stay outside that group on purpose: a UIStroke parented to a CanvasGroup ignores GroupTransparency, so it would not fade with the rest.

Types

SpotlightConfig

interface SpotlightConfig {
Namestring?--

ScreenGui name. Defaults to "TutorialSpotlight"

DisplayOrdernumber?--

ScreenGui DisplayOrder. Defaults to 9700

DimColorColor3?--

Colour of the dimmed area

DimTransparencynumber?--

How much of the screen shows through. Defaults to 0.42

PaddingVector2?--

Default room around the target, in pixels

CornerRadiusnumber?--

Corner radius of the hole. Defaults to 8

StrokeColorColor3?--

Colour of the highlight outline

StrokeThicknessnumber?--

Outline thickness. Defaults to 2.5

StrokeTransparencynumber?--

Outline transparency. Defaults to 0.15

TweenTimenumber?--

Seconds to slide between targets. Defaults to 0.6

Shineboolean?--

Animated gradient on the outline. Defaults to true

ShineColorColor3?--

Colour of the shine

ShineSpeednumber?--

Shine speed. Defaults to 0.35

PointerImagestring?--

Image for the bobbing pointer. No pointer without one

PointerSizeVector2?--

Pointer size in pixels

PointerGapnumber?--

Gap between pointer and target. Defaults to 12

PointerBobDistancenumber?--

How far the pointer bobs. Defaults to 8

PointerBobCyclenumber?--

Seconds per bob. Defaults to 0.5

}

Styling for the overlay. Pass it as Spotlight in TutorialConfig to style a whole tutorial at once:

Tutorial.build(steps, {
	Spotlight = {
		DimTransparency = 0.55,
		StrokeColor = Color3.fromRGB(255, 214, 102),
		PointerImage = "rbxassetid://1234567",
	},
})

Functions

create

constructor
Spotlight.create(
configSpotlightConfig?--

Styling

) → Spotlight

Creates a standalone overlay with its own ScreenGui.

Tutorial.build already does this for you, so call it directly only when you want a highlight outside a tutorial. If you do, you own it: call Spotlight:Destroy yourself.

Errors

TypeDescription
"TutorialKit.Spotlight is client only"Called from the server

Focus

Spotlight:Focus(
targetGuiObject,--

What stays lit

optionsFocusOptions?--

Per-target padding

) → ()

Types

interface FocusOptions {
PaddingVector2?--

Overrides the config padding for this one target

}

Moves the hole onto a target, tweening from wherever it was.

The overlay tracks the target every frame, so it follows a button that animates or a list that scrolls. Calling it again with a different target slides the hole across rather than cutting.

Clear

Spotlight:Clear() → ()

Hides the overlay and stops tracking. Safe to call when nothing is focused.

IsVisible

Spotlight:IsVisible() → boolean--

Whether a target is currently focused

GetOverlay

Spotlight:GetOverlay() → Frame--

The root frame of the overlay

The root frame, which is where you parent anything that must render above the dim.

const caption = Instance.new("TextLabel")
caption.Text = "Press here to open the shop"
caption.Parent = tutorial.Spotlight:GetOverlay()

Destroy

lifecycle
Spotlight:Destroy() → ()

Removes the ScreenGui and stops everything. Tutorial:Destroy already does this for you.

Show raw api
{
    "functions": [
        {
            "name": "Focus",
            "desc": "Moves the hole onto a target, tweening from wherever it was.\n\nThe overlay tracks the target every frame, so it follows a button that animates or a list that\nscrolls. Calling it again with a different target slides the hole across rather than cutting.",
            "params": [
                {
                    "name": "target",
                    "desc": "What stays lit",
                    "lua_type": "GuiObject"
                },
                {
                    "name": "options",
                    "desc": "Per-target padding",
                    "lua_type": "FocusOptions?"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 125,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "Clear",
            "desc": "Hides the overlay and stops tracking. Safe to call when nothing is focused.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 132,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "IsVisible",
            "desc": "",
            "params": [],
            "returns": [
                {
                    "desc": "Whether a target is currently focused",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 138,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "GetOverlay",
            "desc": "The root frame, which is where you parent anything that must render above the dim.\n\n```lua\nconst caption = Instance.new(\"TextLabel\")\ncaption.Text = \"Press here to open the shop\"\ncaption.Parent = tutorial.Spotlight:GetOverlay()\n```",
            "params": [],
            "returns": [
                {
                    "desc": "The root frame of the overlay",
                    "lua_type": "Frame"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 152,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Removes the ScreenGui and stops everything. [Tutorial:Destroy] already does this for you.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "tags": [
                "lifecycle"
            ],
            "source": {
                "line": 160,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "create",
            "desc": "Creates a standalone overlay with its own ScreenGui.\n\n[Tutorial.build] already does this for you, so call it directly only when you want a highlight\noutside a tutorial. If you do, you own it: call [Spotlight:Destroy] yourself.",
            "params": [
                {
                    "name": "config",
                    "desc": "Styling",
                    "lua_type": "SpotlightConfig?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Spotlight"
                }
            ],
            "function_type": "static",
            "tags": [
                "constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"TutorialKit.Spotlight is client only\"",
                    "desc": "Called from the server"
                }
            ],
            "source": {
                "line": 245,
                "path": "src/Guidance/Spotlight.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "SpotlightConfig",
            "desc": "Styling for the overlay. Pass it as `Spotlight` in [TutorialConfig] to style a whole tutorial at\nonce:\n\n```lua\nTutorial.build(steps, {\n\tSpotlight = {\n\t\tDimTransparency = 0.55,\n\t\tStrokeColor = Color3.fromRGB(255, 214, 102),\n\t\tPointerImage = \"rbxassetid://1234567\",\n\t},\n})\n```",
            "fields": [
                {
                    "name": "Name",
                    "lua_type": "string?",
                    "desc": "ScreenGui name. Defaults to \"TutorialSpotlight\""
                },
                {
                    "name": "DisplayOrder",
                    "lua_type": "number?",
                    "desc": "ScreenGui DisplayOrder. Defaults to 9700"
                },
                {
                    "name": "DimColor",
                    "lua_type": "Color3?",
                    "desc": "Colour of the dimmed area"
                },
                {
                    "name": "DimTransparency",
                    "lua_type": "number?",
                    "desc": "How much of the screen shows through. Defaults to 0.42"
                },
                {
                    "name": "Padding",
                    "lua_type": "Vector2?",
                    "desc": "Default room around the target, in pixels"
                },
                {
                    "name": "CornerRadius",
                    "lua_type": "number?",
                    "desc": "Corner radius of the hole. Defaults to 8"
                },
                {
                    "name": "StrokeColor",
                    "lua_type": "Color3?",
                    "desc": "Colour of the highlight outline"
                },
                {
                    "name": "StrokeThickness",
                    "lua_type": "number?",
                    "desc": "Outline thickness. Defaults to 2.5"
                },
                {
                    "name": "StrokeTransparency",
                    "lua_type": "number?",
                    "desc": "Outline transparency. Defaults to 0.15"
                },
                {
                    "name": "TweenTime",
                    "lua_type": "number?",
                    "desc": "Seconds to slide between targets. Defaults to 0.6"
                },
                {
                    "name": "Shine",
                    "lua_type": "boolean?",
                    "desc": "Animated gradient on the outline. Defaults to true"
                },
                {
                    "name": "ShineColor",
                    "lua_type": "Color3?",
                    "desc": "Colour of the shine"
                },
                {
                    "name": "ShineSpeed",
                    "lua_type": "number?",
                    "desc": "Shine speed. Defaults to 0.35"
                },
                {
                    "name": "PointerImage",
                    "lua_type": "string?",
                    "desc": "Image for the bobbing pointer. No pointer without one"
                },
                {
                    "name": "PointerSize",
                    "lua_type": "Vector2?",
                    "desc": "Pointer size in pixels"
                },
                {
                    "name": "PointerGap",
                    "lua_type": "number?",
                    "desc": "Gap between pointer and target. Defaults to 12"
                },
                {
                    "name": "PointerBobDistance",
                    "lua_type": "number?",
                    "desc": "How far the pointer bobs. Defaults to 8"
                },
                {
                    "name": "PointerBobCycle",
                    "lua_type": "number?",
                    "desc": "Seconds per bob. Defaults to 0.5"
                }
            ],
            "source": {
                "line": 84,
                "path": "src/Guidance/Spotlight.luau"
            }
        },
        {
            "name": "FocusOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Padding",
                    "lua_type": "Vector2?",
                    "desc": "Overrides the config padding for this one target"
                }
            ],
            "source": {
                "line": 110,
                "path": "src/Guidance/Spotlight.luau"
            }
        }
    ],
    "name": "Spotlight",
    "desc": "Dims the screen and cuts a hole around a GuiObject.\n\nYou normally reach this through [FocusUI], which drives it for you. Use it directly only when\nyou want the highlight without a step around it.\n\n```lua\nconst spotlight = TutorialKit.Spotlight.create({ DimTransparency = 0.5 })\n\nspotlight:Focus(shopButton)\ntask.wait(3)\nspotlight:Clear()\n```\n\n### Why the panels live in a CanvasGroup\n\nThe obvious build for \"dim everything except this rectangle\" is four semi-transparent frames\naround a hole. Their alpha stacks wherever they touch, and that overlap renders as a visible\nseam along the edge of the hole. Padding cannot fix it, because the problem is overlap, not\nspacing.\n\nA `CanvasGroup` flattens its descendants into a single image and applies `GroupTransparency` to\nthe result, so the panels can be fully opaque and overlap freely without ever double-darkening a\npixel.\n\nThe highlight stroke and the pointer stay outside that group on purpose: a `UIStroke` parented to\na `CanvasGroup` ignores `GroupTransparency`, so it would not fade with the rest.",
    "realm": [
        "Client"
    ],
    "source": {
        "line": 33,
        "path": "src/Guidance/Spotlight.luau"
    }
}