AutoStroke Module

My name is TheVerseer and I made a new module called AutoStroke this module is very useful for UI designers.

The Problem: When making UI using UIStrokes, its hard to make it look good on mobile and other devices, The biggest problem is something roblox could easily fix, for example the UICorner Instance has the ability to use Scale rather than offset, but UIStrokes dont which sucks, for multiple reasons, BillboardGUIs with UIStrokes and ScreenGUIs with uistrokes both suffer for the same reason, on smaller screens those UIStrokes don’t automatically change.

The Solution: My module, known as AutoStroke uses a very simple formula to automatically update the UIStroke on all devices



From this:
Screenshot 2024-06-06 123628
To this:

From this:
Screenshot 2024-06-06 124147
To this:
Screenshot 2024-06-06 124241

Theres 2 ways to implement this into your game:

Method 1: (Easier)

  1. Create a LocalScript, and put itside either a ScreenGUI or a BillboardGUI
  2. copy and paste this code into the script:

Method 2: (Harder)

  1. Get this model:
  2. Insert this model into your game, and unload the contents of the folder into ReplicatedFirst
  3. Select all GUIs you want to affect, and add this tag: AutoStroke_Registered

If you have any suggestions for this model, please tell me down below :slight_smile:


For clarification:

In the video, it seems to be moving very choppily, that has nothing to do with a module itself, my pc is just bad.

1 Like

This module works on billboards ?

1 Like


This doesn’t work cause LocalScripts can’t use require() on external assets. To fix this you can simply grab the module from the creator store and manually put it inside your LocalScript and require it using require(script.MainModule).

Hello, I have remade the module to be more optimized and with clean code :sunglasses: (the original module was using getdescendants every heartbeat + setting up a lot of hearbeat connections)

--!optimize 2

local AutoStroke = {}
local RunService = game:GetService("RunService")

local ScreenGuis : {UIStroke} = {}

local BillboardGuis : {
	[UIStroke] : {
		MaxDistance : number,
		GUI : BillboardGui,
} = {}

local OriginalThickness : {[UIStroke] : number} = {}
local BaseSize = 1920 + 1080

local Camera = workspace.CurrentCamera

local function GetCharacter(Player : Player) : Model
	return Player.Character or Player.CharacterAdded:Wait()

local function GetPlayerDistanceFromBillboardGui(Player : Player, GUI : BillboardGui) : number
	local Character = GetCharacter(Player)

	local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart") :: BasePart
	local Adornee = GUI.Adornee :: BasePart

	return (HumanoidRootPart.Position - Adornee.Position).Magnitude

local function GetCameraDistanceFromBillboardGui(GUI : BillboardGui) : number
	local Adornee = GUI.Adornee :: BasePart

	return (Camera.CFrame.Position - Adornee.Position).Magnitude

function AutoStroke:Register(GUI : ScreenGui | BillboardGui, MaxDistance : number)
	if GUI:IsA("ScreenGui") then
		for _, v in GUI:GetDescendants() do
			if not v:IsA("UIStroke") then continue end
			OriginalThickness[v] = v.Thickness
			table.insert(ScreenGuis, v)
	elseif GUI:IsA("BillboardGui") then
		MaxDistance = MaxDistance or 30

		for _, v in GUI:GetDescendants() do
			if not v:IsA("UIStroke") then continue end

			OriginalThickness[v] = v.Thickness
			BillboardGuis[v] = {
				GUI = GUI,
				MaxDistance = MaxDistance,

function AutoStroke:Init()
	RunService:BindToRenderStep("AutoStrokeUpdate", Enum.RenderPriority.Camera.Value, function()
		local ViewportSize = Camera.ViewportSize
		local CurrentSize = ViewportSize.X + ViewportSize.ViewportSize.Y

		for _, Stroke in ScreenGuis do
			local OriginalSize = OriginalThickness[Stroke]

			Stroke.Thickness = math.clamp(OriginalSize / (BaseSize / CurrentSize), 0, OriginalSize)

		for Stroke, Info in BillboardGuis do
			local Distance = GetCameraDistanceFromBillboardGui(Info.GUI)
			local Original = OriginalThickness[Stroke]

			Stroke.Thickness = math.clamp(Original / (Distance / Info.MaxDistance), 0, Original)

return AutoStroke

feel free to change anything