SevenSegment | Create 7-segment displays with ease!

The SevenSegment Module!

Welcome to SevenSegment. A very light-weight UI module that allows you to create highly customisable and performant 7-segment displays in roblox with little to no effort!

No more do you have to fake this effect with static images, or use fonts that are close enough, as you can now have the real thing that can be treated like text with heaps of customisability!

ec63a1d81cd87bbb79b375e69d4ec4ac

A display can be created and have a row of 7-segment characters that can display all 10 numbers along a few letters and symbols. A display also has many properties and methods to change it’s current displaying characters, layout and appearance!


Previews

Here’s a couple of example usages:


Digital Alarm Clock Display


Stopwatch


Get SevenSegment

SevenSegment Demo Place.rbxl (56.6 KB)


How to use?

Using the SevenSegment module to create 7-segment displays can be really simple and quick! All you need is a GUI or SurfaceGUI with a frame positioned and scaled for your display.

Basic Usage
local SevenSegment = require(Gui.SevenSegment) -- The module itself

local Display = SevenSegment.new(Gui.Frame) -- Creates a default white single digit display
Display:Set(4) -- Display the number 4

Here we have just created a barebones single digit 7-segment display showing a number 4. Incredible!
image

Now for most people, 1 digit isn’t enough, so if we want to, we can add an optional paramter when we create our display to include more usable digits:

local SevenSegment = require(Gui.SevenSegment) -- The module itself

local Display = SevenSegment.new(Gui.Frame, 5) -- Creates a 5 digit display

--Display:Set(4) -- This method is only good for setting a single selected digit
Display:SetText(12345) -- Display a range of numbers

image

Now we have a nice 5 digit display showing 5 numbers

Customising Displays

With the SevenSegment.new() function, there is a third optional parameter you can use which lets you customise the appearance of your display with a DisplayInfo object, which is a table of your desired settings!

Here’s a nice example:

local SevenSegment = require(Gui.SevenSegment) -- The module itself

local DisplayInfo = {
	OnColour = Color3.new(1, 0.3, 0.3), -- Segment on colour
	OffColour = Color3.new(0.3), -- Segment off colour (instead of being invisible)
	Italics = true, -- Skews the digits
}

-- Creates a typical italics red display like a digital alarm clock!
local Display = SevenSegment.new(Gui.Frame, 5, DisplayInfo) 

Display:SetText(12345)

image

If you wanted to go further, you can also add or remove as many valid parameters in that DisplayInfo table as you desire.

Here are all the current valid parameters of a DisplayInfo table that you can use. The values you see are the internal default values:

DisplayInfo = {
	OnColour = Color3.new(1, 1, 1), -- Lit segments colour
	OffColour = nil, -- The unlit segment colours of the. Hides the segments instead if empty
	Thickness = nil, -- In pixels. Automatically scales dynamically when empty
	SeamSize = 1.5, -- A percentage based off Thickness.
	Italics = false, -- Italics/skewed style
	HorizontalAlignment = Enum.HorizontalAlignment.Left, -- Where the digits get aligned
	LayoutSpacing = UDim.new(0, 15) -- The padding between each digit
}

Here’s a really cool segment display I made that uses all parameters displaying the word HELLO:

Code
local SevenSegment = require(Gui.SevenSegment) -- The module itself

DisplayInfo = {
	OnColour = Color3.new(1, 1, 0.4),
	OffColour = Color3.new(0.2, 0.2, 0),
	Thickness = 4,
	SeamSize = 1,
	Italics = true,
	HorizontalAlignment = Enum.HorizontalAlignment.Center,
	LayoutSpacing = UDim.new(0.1, 0)
}

local Display = SevenSegment.new(Gui.Frame, 5, DisplayInfo)

Display:SetText("HELLO") -- Supports a few letters!
66 Likes

How do I get this to work?

local Gui = script.Parent
local Frame = Gui:WaitForChild("MainFrame")


local SevenSegment = require(Gui:WaitForChild("SevenSegment"))

local Display = SevenSegment.new(Gui.Frame.DisplayA, 2) -- Creates a 5 digit display

--Display:Set(4) -- This method is only good for setting a single selected digit
Display:SetText(99) -- Display a range of numbers

1 Like

Your script looks like it is under workspace and your using a local script instead of a server script. Local scripts cannot run under workspace.

Change the local script to be a regular script instead or move the script to a client-based container. Such as starter character scripts, or starter gui, etc

1 Like

Fixed a few issues & added slight improvements. Nothin’ major.

--[[
	================== SevenSegment ===================
	
	Created by: Ethanthegrand (@Ethanthegrand14)
	
	Last updated: 14/08/2023
	Version: 1.0.0
	
	Learn how to use the module here: https://devforum.roblox.com/t/2526209
	
	====================== API ========================
	
	> SevenSegment Module Functions
	
		SevenSegment.new(Frame : GuiObject, Digits : number?, DisplayInfo : table?)
			- Creates and returns a new Display object with an optional amount of digits and a DisplayInfo
			  to affect appearance.
	
	
	> Display Object Methods
	
		Display:Set(Character : number | string, DigitIndex: number?)
			- Changes the selected digit/character in the row.
			- If DigitIndex is empty, it will default to the first 
			  digit in your display.
		
		Display:SetText(Text : string | number)
			- Changes the entire row of characters from a given string or number.
		
		Display:SetColours(OnColour : Color3, OffColour : Color3?, DigitIndex: number?)
			- Changes the selected digit segment colours.
			- If OffColour is left empty or set to nil, it will use visibility instead of a colour.
			- If DigitIndex is left empty, then all digits will be affected
			
		Display:SetVisibility(Visible : boolean, DigitIndex: number?)
			- Changes the selected digit segment colours.
			- If OffColour is left empty or set to nil, it will use visibility instead of a colour.
			- If DigitIndex is left empty, then all digits will be affected
		
		Display:Destroy()
			- Destroys the Display object and all related instances and frames
			  that were created.
		
	
	=============== DisplayInfo ================
	
	Here are all the valid parameters of a DisplayInfo table which you can feed into the
	SevenSegment.new() function to customise your display:
	
	DisplayInfo = {
		OnColour : Color3,
		OffColour : Color3,
		Thickness : number,
		SeamSize : number,
		Italics : boolean,
		HorizontalAlignment : Enum.HorizontalAlignment,
		LayoutSpacing : UDim
	}
	
]]

type DisplayInfoType = {
	OnColour: Color3,
	OffColour: Color3?,
	Thickness: number?,
	SeamSize: number?,
	Italics: boolean?,
	HorizontalAlignment: Enum.HorizontalAlignment?,
	LayoutSpacing: UDim?
}

local MainModule = {}

local CharacterSheet = require(script:WaitForChild("CharacterSheet"))
local SegmentDisplayTemplate = script:WaitForChild("SegmentDisplayTemplate")
local SegmentDisplayItalicsTemplate = script:WaitForChild("SegmentDisplayItalicsTemplate")

function MainModule.new(Frame: GuiObject, Digits: number?, DisplayInfo: DisplayInfoType?)
	assert(Frame and Frame:IsA("GuiObject"), "Module Error - Argument 1. Please provide a valid GuiObject")
	
	local RowContainer: Frame = Instance.new("Frame")
	RowContainer.AutomaticSize = Enum.AutomaticSize.X
	RowContainer.BackgroundTransparency = 1
	RowContainer.Size = UDim2.fromScale(1, 1)
	RowContainer.Name = "SegmentDisplayRow"
	RowContainer.Parent = Frame
	RowContainer.Visible = true
	
	-- Set up main display properties
	local Thickness: number = math.ceil(RowContainer.AbsoluteSize.Y / 12)
	local ItalicsEnabled: boolean = false
	local SeamSize: number = 1.5
	local SegmentOnColour: Color3 = Color3.new(1, 1, 1)
	local SegmentOffColour: Color3
	local HorizontalAlignment: Enum.HorizontalAlignment = Enum.HorizontalAlignment.Left
	local LayoutSpacing: UDim = UDim.new(0, 15)
	
	local AutomaticScaling = not DisplayInfo or not DisplayInfo.Thickness
	
	if DisplayInfo then
		-- Segment colouring
		SegmentOnColour = DisplayInfo.OnColour or SegmentOnColour
		SegmentOffColour = DisplayInfo.OffColour or SegmentOffColour
		
		-- Segment sizing and offset
		Thickness = DisplayInfo.Thickness or Thickness
		SeamSize = DisplayInfo.SeamSize or SeamSize
		
		ItalicsEnabled = DisplayInfo.Italics or ItalicsEnabled
		
		HorizontalAlignment = DisplayInfo.HorizontalAlignment or HorizontalAlignment
		LayoutSpacing = DisplayInfo.LayoutSpacing or LayoutSpacing
	end
	
	-- Create display(s)
	local SegmentDisplay = {
		Frame = Frame,
		DigitCount = Digits or 1,
		
		-- Properties
		OnColour = SegmentOnColour,
		OffColour = SegmentOffColour,
		
		-- Internal variables
		CurrentDisplays = {}
	}

	local ListLayout = Instance.new("UIListLayout")
	ListLayout.FillDirection = Enum.FillDirection.Horizontal
	ListLayout.HorizontalAlignment = HorizontalAlignment or Enum.HorizontalAlignment.Left
	ListLayout.Padding = LayoutSpacing
	ListLayout.SortOrder = Enum.SortOrder.LayoutOrder
	ListLayout.Parent = RowContainer
	
	local TemplateToUse
	
	if ItalicsEnabled then
		TemplateToUse = SegmentDisplayItalicsTemplate
	else
		TemplateToUse = SegmentDisplayTemplate
	end
	
	local function AdjustSegmentFrames(FramesTable)
		local AbsoluteThickness = Thickness
		local PixelSeamSize = (AbsoluteThickness / 2) * SeamSize
		
		for i, Segment in ipairs(FramesTable) do
			if i == 1 or i == 4 or i == 7 then 
				-- Horizontal segments
				Segment.Size = UDim2.new(1, -AbsoluteThickness, 0, AbsoluteThickness)
			else
				Segment.Size = UDim2.new(1, -PixelSeamSize, 0, AbsoluteThickness)
			end
		end

		-- Adjust vertical segment proportions
		FramesTable[2].Position = UDim2.new(1, -AbsoluteThickness / 2, 0.25, AbsoluteThickness / 4)
		FramesTable[6].Position = UDim2.new(0, AbsoluteThickness / 2, 0.25, AbsoluteThickness / 4)

		FramesTable[3].Position = UDim2.new(1, -AbsoluteThickness / 2, 0.75, -AbsoluteThickness / 4)
		FramesTable[5].Position = UDim2.new(0, AbsoluteThickness / 2, 0.75, -AbsoluteThickness / 4)
	end
	
	if AutomaticScaling then
		Thickness = math.ceil(RowContainer.AbsoluteSize.Y / 12)
	end
	
	for i = 1, SegmentDisplay.DigitCount do
		local DisplayFrame = TemplateToUse:Clone()
		DisplayFrame.Name = "SegmentDisplay" .. i
		DisplayFrame.Parent = RowContainer
		DisplayFrame.LayoutOrder = i
		DisplayFrame.Visible = true

		local SegmentFrames = {
			DisplayFrame.TopSegment,
			DisplayFrame.TopRightSegment,
			DisplayFrame.BottomRightSegment,
			DisplayFrame.BottomSegment,
			DisplayFrame.BottomLeftSegment,
			DisplayFrame.TopLeftSegment,
			DisplayFrame.MiddleSegment,
		}
		
		local DisplayProperties = { -- Individual seven segment display
			EnabledSegments = {false, false, false, false, false, false, false},
			OnColour = SegmentOnColour,
			OffColour = SegmentOffColour,
			SegmentFrames = SegmentFrames,
			IsVisible = true
		}
		
		for i, Segment in ipairs(SegmentFrames) do
			if SegmentOffColour then
				Segment.ImageColor3 = SegmentOffColour
			else
				Segment.Visible = false
			end
		end

		AdjustSegmentFrames(SegmentFrames)

		table.insert(SegmentDisplay.CurrentDisplays, DisplayProperties)	
	end
	
	local AutoScaleConnection: RBXScriptConnection
	
	-- Automatically rescale the segments
	if AutomaticScaling then
		AutoScaleConnection = RowContainer:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
			Thickness = math.ceil(RowContainer.AbsoluteSize.Y / 12)
			
			for i = 1, SegmentDisplay.DigitCount do
				AdjustSegmentFrames(SegmentDisplay.CurrentDisplays[i].SegmentFrames)
			end
		end)
	end
	
	-- Methods
	function SegmentDisplay:Set(Character: number | string, DigitIndex: number?)
		DigitIndex = DigitIndex or 1
		
		if DigitIndex > #self.CurrentDisplays then return end
		
		assert(Character and tostring(Character), "Module Error - Arugment 1. Invalid number/string")
		assert(
			DigitIndex and tonumber(DigitIndex) and math.floor(DigitIndex) == DigitIndex, 
			"Module Error - Argument 2. Invalid value. DisplayPosition must be an interger"
		)
		
		local Display = self.CurrentDisplays[DigitIndex]
		
		if not Display.IsVisible then return end

		local SegmentData = CharacterSheet[tostring(Character)] -- Number, symbol, letter

		if not SegmentData then
			SegmentData = CharacterSheet[tostring(Character):lower()] -- lowercase letter
		end
		
		if not SegmentData then
			-- Invalid character
			for i, Segment in ipairs(Display.SegmentFrames) do
				if self.OffColour then
					Segment.ImageColor3 = self.OffColour
				else
					Segment.Visible = false
				end
			end
			return
		end
		
		-- Set the segments
		for i, Segment in ipairs(Display.SegmentFrames) do
			local Lit = SegmentData[i] and SegmentData[i] == 1
			
			Display.EnabledSegments[i] = Lit

			if Lit then
				Segment.Visible = true
				Segment.ImageColor3 = self.OnColour
			elseif self.OffColour then
				Segment.ImageColor3 = self.OffColour
			else
				Segment.Visible = false
			end
		end
	end

	function SegmentDisplay:SetText(Text: string | number)
		local StringText: string = ""

		assert(Text and tostring(Text), "Module Error - Argument 1. Please provide a valid string or number")

		if typeof(Text) == "number" then
			StringText = tostring(math.floor(Text))  -- convert the floored number to a string
		else
			StringText = tostring(Text)
		end

		local Characters = string.split(StringText, "")

		for i = 1, #self.CurrentDisplays do
			if Characters[i] then
				self:Set(Characters[i], i)
			else
				self:Set("", i)
			end
		end
	end
	
	function SegmentDisplay:SetColours(OnColour: Color3, OffColour: Color3?, DigitIndex: number?)	
		assert(OnColour and typeof(OnColour) == "Color3", "Module Error - Argument 1. Please provide a valid Color3 value")
		assert(not OnColour or typeof(OnColour) == "Color3", "Module Error - Argument 2. Please provide a valid Color3 value")
		
		local function AdjustDisplay(Display)
			self.OnColour = OnColour
			
			if OffColour then
				self.OffColour = OffColour
			end
			
			if Display.IsVisible then
				for i, SegmentFrame in ipairs(Display.SegmentFrames) do
					if Display.EnabledSegments[i] then
						SegmentFrame.ImageColor3 = OnColour
					elseif OffColour then
						SegmentFrame.ImageColor3 = OffColour
						SegmentFrame.Visible = true
					end
				end
			end
		end
		
		if DigitIndex then	
			AdjustDisplay(self.CurrentDisplays[DigitIndex])
		else
			for i, Display in ipairs(self.CurrentDisplays) do
				AdjustDisplay(self.CurrentDisplays[i])
			end
		end
	end
	
	function SegmentDisplay:SetVisibility(Visible: boolean, DigitIndex: number?)	
		local function SetDisplay(Display)
			for i: number, Segment in ipairs(Display.SegmentFrames) do
				
				if Display.EnabledSegments[i] and Visible then
					Segment.ImageColor3 = self.OnColour
					Segment.Visible = true
				elseif self.OffColour and Visible then
					Segment.ImageColor3 = self.OffColour
					Segment.Visible = true
				else
					Segment.Visible = false
					Segment.ImageColor3 = self.OnColour
				end
				
			end
			
			Display.IsVisible = Visible
		end
		
		if DigitIndex then	-- Selected display
			SetDisplay(self.CurrentDisplays[DigitIndex])
		else -- Whole row
			for i, Display in ipairs(self.CurrentDisplays) do
				SetDisplay(self.CurrentDisplays[i])
			end
		end
	end
	
	function SegmentDisplay:Destroy()
		if AutoScaleConnection then
			AutoScaleConnection:Disconnect()
		end
		
		RowContainer:Destroy()
		
		table.clear(SegmentDisplay.CurrentDisplays)
		table.clear(SegmentDisplay)
	end
	
	return SegmentDisplay
end

return MainModule
3 Likes

oh, very nice. Thank you very much for your contribution, I have updated the module.

This was very much needed too.

2 Likes

I have no idea why you made this, but it is very cool and I might use it.

Awesome. I hope more people stumble across this, because as subtle as it may seem, this is very useful.

Thank you for making this!

1 Like

whats need of putting module in every single object you need to have 7sg???

and how did u make stopwatch? like i mean the : and . symbols?

I just used regular text labels as dots cannot be displayed on the regular segments

@Ethanthegrand14
I just found out that this module won’t work in a while task.wait() do and workspace.Value.Changed scripts. Big sadge :frowning:
No matter what i do it sets the segments and when the value is changed, they do not change at all. Any idea why this happens? If not please change your module to work that way, possibly.

That’s odd. Can you show me one of your scripts you used?

It shouldn’t matter how/when you update the text, it should just work either way

I misunderstood, my bad haha. :laughing: It seems that the math.floor() function breaks it for whatever reason that is. Should i use other methods of rounding up or will you fix it? :thinking:

show me the line of code that does it, because if you use :SetText() or :Set() it should work fine for math.floor() numbers.