Start typing to search packages!
numberspinnerv2
By @biotoxin495
Roblox
MirroredNumberSpinnerV2 — A modern animated number and text spinner module
NumberSpinnerV2, a modern animated number and text spinner utility module for Roblox UI.
NumberSpinnerV2 was inspired by NumberSpinner by boatbomber. However, using and extending older spinner implementations often meant maintaining separate value-update logic, manually synchronizing TextLabel properties, and working around limited TextScaled support.
NumberSpinnerV2 addresses these pain points. It is built around the same general concept, but has been rewritten from scratch and expanded into a more complete, responsive, and configurable system.
The underlying spinner system has been used internally in my projects for the past two years, and I have now polished it into an open-source module for public use.
Check out the uncopylocked showcase game on Roblox:
Video showcase:
https://www.youtube.com/watch?v=_erdwv5Xg_s
Quick example
NumberSpinnerV2 attaches directly to a normal TextLabel.
local NumberSpinnerV2 = require(ReplicatedStorage.Modules.NumberSpinnerV2)
local spinner = NumberSpinnerV2.Attach(label, {
Separator = ",",
Prefix = "$",
})
label.Text = 1250
You continue updating the original TextLabel.Text property. NumberSpinnerV2 handles the animation, formatting, layout, and responsive sizing.
🚀 Features
TextLabel-driven workflow
NumberSpinnerV2 attaches directly to an existing TextLabel.
local spinner = NumberSpinnerV2.Attach(myTextLabel, options)
myTextLabel.Text = 1250
myTextLabel.Text = 50000
myTextLabel.Text = 1234567
The source TextLabel remains the public interface. The module hides its original rendered text and creates the animated spinner UI inside it.
This allows you to build and style your UI normally in Roblox Studio without maintaining a separate display pipeline.
TextScaled support
NumberSpinnerV2 supports TextScaled.
The module measures and resizes its generated characters based on the source TextLabel, allowing the spinner to respond when the label or its container changes size.
This was one of the main reasons I created the module.
Spin modes
NumberSpinnerV2 includes multiple animation modes through the SpinMode option.
Full
"Full" is the default mode. Each slot uses its available character pages and moves to the target character.
NumberSpinnerV2.Attach(label, {
SpinMode = "Full",
})
SingleStep
"SingleStep" transitions directly from the currently displayed character to the next character.
NumberSpinnerV2.Attach(label, {
SpinMode = "SingleStep",
})
Reel
"Reel" creates a slot-machine-like animation using a character reel.
CharacterReel accepts either a string or an ordered array of characters.
NumberSpinnerV2.Attach(label, {
SpinMode = "Reel",
CharacterReel = "0123456789",
ReelStepDuration = 0.04,
ReelMaxFinishSpread = 1,
})
If no custom reel is provided, the module uses its built-in number, punctuation, and letter reel.
Number formatting
NumberSpinnerV2 supports common number-formatting options:
- Thousands separators
- Decimal places
- Forced decimals
- Abbreviations
- Abbreviation-specific decimal places
- Custom decimal separators
Example:
NumberSpinnerV2.Attach(label, {
Separator = ",",
DecimalPlaces = 2,
ForceDecimals = true,
})
label.Text = 1250
-- Displays: 1,250.00
Abbreviations
You can define custom abbreviation thresholds:
NumberSpinnerV2.Attach(label, {
Abbreviations = {
[1e3] = "K",
[1e6] = "M",
[1e9] = "B",
},
AbbreviationDecimalPlaces = 1,
})
Example displays:
950
1.2K
48.5K
3.6M
7.1B
Prefixes, suffixes, and icon affixes
NumberSpinnerV2 supports text prefixes and suffixes:
NumberSpinnerV2.Attach(label, {
Prefix = "$",
Suffix = " Coins",
})
It also supports image affixes:
NumberSpinnerV2.Attach(label, {
IconPrefix = "rbxassetid://0000000000",
Suffix = " Coins",
AffixSpacing = 6,
})
If IconPrefix is set, it takes priority over the text Prefix.
If IconSuffix is set, it takes priority over the text Suffix.
Animation configuration
The main spin animation can be configured through standard Roblox easing options:
NumberSpinnerV2.Attach(label, {
Duration = 0.5,
EasingStyle = Enum.EasingStyle.Quad,
EasingDirection = Enum.EasingDirection.Out,
})
This allows the animation to be smooth, fast, bouncy, or snappy depending on the use case.
Bounce effect
NumberSpinnerV2 can optionally apply a small pop animation to the entire spinner when its displayed value changes:
NumberSpinnerV2.Attach(label, {
BounceOnChange = true,
BounceScale = 1.1,
})
This works well for rewards, coins, damage values, level-ups, and similar UI feedback.
UIStroke inheritance
If the source TextLabel contains a UIStroke, NumberSpinnerV2 can clone it onto the generated characters:
NumberSpinnerV2.Attach(label, {
ApplyStrokeToChars = true,
})
This helps the spinner preserve the visual style of the original label.
Roman numeral and text modes
The default mode formats numeric input, but NumberSpinnerV2 also supports Roman numerals and general text or character spinning.
Roman numeral example:
NumberSpinnerV2.Attach(label, {
Mode = "Roman",
Prefix = "Level ",
})
label.Text = 9
-- Displays: Level IX
Text or rank example:
NumberSpinnerV2.Attach(label, {
Mode = "Text",
Prefix = "Rank ",
})
label.Text = "S"
-- Displays: Rank S
These modes make the module usable for levels, ranks, short labels, and other non-standard counters.
📖 Basic usage
Place the NumberSpinnerV2 module somewhere accessible to a client script. Require the module and attach it to a TextLabel:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local NumberSpinnerV2 = require(ReplicatedStorage.Modules.NumberSpinnerV2)
local label = script.Parent.AmountLabel
local spinner = NumberSpinnerV2.Attach(label, {
Duration = 0.5,
EasingStyle = Enum.EasingStyle.Quad,
EasingDirection = Enum.EasingDirection.Out,
Separator = ",",
DecimalPlaces = 0,
ForceDecimals = false,
Prefix = "$",
Suffix = "",
})
Update the original label normally:
label.Text = 0
label.Text = 1250
label.Text = 50000
The module listens to TextLabel.Text and handles the visual update.
Example: leaderstats coin counter
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local NumberSpinnerV2 = require(ReplicatedStorage.Modules.NumberSpinnerV2)
local label = script.Parent.CoinsLabel
local spinner = NumberSpinnerV2.Attach(label, {
Duration = 0.5,
EasingStyle = Enum.EasingStyle.Quad,
EasingDirection = Enum.EasingDirection.Out,
Separator = ",",
DecimalPlaces = 0,
IconPrefix = "rbxassetid://0000000000",
Suffix = " Coins",
AffixSpacing = 6,
ApplyStrokeToChars = true,
BounceOnChange = true,
BounceScale = 1.08,
})
local leaderstats = player:WaitForChild("leaderstats")
local coins = leaderstats:WaitForChild("Coins")
local function updateCoins(value)
label.Text = value
end
updateCoins(coins.Value)
coins.Changed:Connect(updateCoins)
Example: abbreviated large values
local spinner = NumberSpinnerV2.Attach(label, {
Separator = ",",
Abbreviations = {
[1e3] = "K",
[1e6] = "M",
[1e9] = "B",
},
AbbreviationDecimalPlaces = 1,
})
label.Text = 1260000
-- Displays: 1.3M
Example: currency display
local spinner = NumberSpinnerV2.Attach(label, {
Prefix = "$",
Separator = ",",
DecimalPlaces = 2,
ForceDecimals = true,
})
label.Text = 1250
-- Displays: $1,250.00
Example: Roman numeral levels
local spinner = NumberSpinnerV2.Attach(label, {
Mode = "Roman",
Prefix = "Level ",
Duration = 0.35,
})
label.Text = 4
-- Displays: Level IV
label.Text = 9
-- Displays: Level IX
label.Text = 10
-- Displays: Level X
⚙️ API
NumberSpinnerV2.Attach(textLabel, options)
Attaches a spinner to a TextLabel and returns its controller.
local spinner = NumberSpinnerV2.Attach(label, options)
NumberSpinnerV2.createSpinningNumber(textLabel, options)
An alias for Attach, included for compatibility with the older usage pattern.
local spinner = NumberSpinnerV2.createSpinningNumber(label, options)
NumberSpinnerV2.FormatValue(value, options?)
Formats a number using the module's number-formatting options without creating a spinner.
local formatted = NumberSpinnerV2.FormatValue(1260000, {
Separator = ",",
Abbreviations = {
[1e3] = "K",
[1e6] = "M",
},
AbbreviationDecimalPlaces = 1,
})
-- "1.3M"
NumberSpinnerV2.ToRoman(value, options?)
Converts a number into a Roman numeral without creating a spinner.
local roman = NumberSpinnerV2.ToRoman(9)
-- "IX"
NumberSpinnerV2.DEFAULT_OPTIONS
A copy of the module's default option values.
It can be used as a reference or starting point. Changing this table does not alter the internal defaults used by future spinners.
spinner:SetValue(value, animate?)
Sets the source label text.
It animates by default. Pass false to update without animation.
spinner:SetValue(5000)
spinner:SetValue(5000, false)
This is equivalent to updating the source label:
label.Text = 5000
spinner:SetText(text, animate?)
Sets the source label text using a text value.
It animates by default. Pass false to update without animation.
spinner:SetText("S")
spinner:SetText("S", false)
spinner:SetOptions(options, animate?)
Merges new options into the spinner's current options and redraws it.
Pass true to animate the redraw.
spinner:SetOptions({
BounceOnChange = true,
Duration = 0.25,
})
spinner:Refresh(animate?)
Forces the spinner to re-measure and redraw.
Pass true to animate the redraw.
spinner:Refresh()
spinner:Destroy()
Cleans up the generated UI and restores the original TextLabel text visibility.
spinner:Destroy()
Complete options reference
You normally only need to provide the options you want to change. Any omitted options use the module defaults.
{
-- Animation
Duration = 0.5,
SlotResizeDuration = 0.08,
SpinMode = "Full", -- "Full", "SingleStep", or "Reel"
ReelStepDuration = nil,
ReelMaxFinishSpread = 1,
EasingStyle = Enum.EasingStyle.Quad,
EasingDirection = Enum.EasingDirection.Out,
Circular = false,
-- Formatting
Mode = "Number",
Separator = ",",
DecimalPlaces = 0,
ForceDecimals = false,
DecimalSeparator = ".",
Abbreviations = {},
AbbreviationDecimalPlaces = 1,
ForceAbbreviationDecimals = false,
-- Roman formatting
RomanZero = "N",
RomanMax = 3999,
RomanAllowLarge = false,
-- Affixes
Prefix = "",
Suffix = "",
IconPrefix = "",
IconSuffix = "",
AffixSpacing = 0,
IconAffixSpacing = 4,
-- Character layout
CharacterPadding = 2,
PunctuationPaddingScale = 1.5,
PunctuationWidthScale = 0.45,
LetterWidthPadding = 5,
LetterEdgeBleed = 2,
UniformLetterSlots = false,
SymbolWidthPadding = 1,
SlotAlignment = "Auto",
CharacterOrder = nil,
CharacterReel = nil,
-- Visual styling
ApplyStrokeToChars = true,
-- Bounce
BounceOnChange = false,
BounceScale = 1.08,
BounceDuration = 0.12,
BounceReturnDuration = 0.14,
BounceEasingStyle = Enum.EasingStyle.Quad,
BounceEasingDirection = Enum.EasingDirection.Out,
-- Accessibility
RespectReducedMotion = true,
-- TextScaled bounds
MinTextSize = 1,
MaxTextSize = 100,
-- Advanced
Formatter = nil,
}
Display modes
"Number"
The default mode.
It attempts to parse TextLabel.Text as a number and applies the configured number formatting.
Mode = "Number"
"Roman"
Converts numeric input into Roman numerals.
Mode = "Roman"
"Text"
Displays and spins general text or characters without numeric formatting.
Mode = "Text"
"Characters"
Equivalent to "Text".
The singular spelling "Character" is also accepted.
Mode = "Characters"
Animation options
SpinMode = "Full"uses the normal cached character pages and moves each slot to its target character. This is the default.SpinMode = "SingleStep"transitions from the currently displayed character directly to the next character.SpinMode = "Reel"plays each slot throughCharacterReelbefore landing on its target.SlotResizeDurationcontrols the width-change animation when a character slot changes size.ReelStepDuration, when set to a value greater than zero, derives the reel animation duration from its number of steps.ReelMaxFinishSpreadlimits the additional finish-time spread used by reel animations.Circularapplies to the normal"Full"spin mode. Reel and single-step animations use a non-circular sequence.
Character layout options
CharacterPaddingadds horizontal spacing around characters.PunctuationPaddingScaleadjusts the spacing applied to punctuation.PunctuationWidthScaleadjusts the measured width of punctuation slots.LetterWidthPaddingadds additional width for letter slots.LetterEdgeBleedhelps prevent letter edges from appearing clipped.UniformLetterSlotsgives letter slots a stable, uniform width.SymbolWidthPaddingadds additional width around symbols.SlotAlignmentaccepts"Auto","Left", or"Right".- Automatic slot alignment is right-aligned for numeric input and left-aligned for other input.
CharacterOrderaccepts an ordered character array and controls page ordering in"Full"mode.CharacterReelaccepts a string or ordered character array and is used bySpinMode = "Reel".
📝 Notes
- NumberSpinnerV2 is intended for client-side UI.
- The original
TextLabelcan be designed and styled normally in Roblox Studio. - The generated spinner UI is created inside the source
TextLabel. - Updating
TextLabel.Textis the main usage pattern. TextScaledis supported.- If you use icon affixes, make sure the supplied asset IDs refer to valid image assets.
- For extremely small or responsive UI, consider adding a
UITextSizeConstraintto the source label. - Call
spinner:Destroy()when you no longer need the spinner and want to restore the original label rendering.
🛠️ Installation
Wally
Add NumberSpinnerV2 to your wally.toml dependencies:
[dependencies]
NumberSpinnerV2 = "biotoxin495/numberspinnerv2@1.2.0"
Run:
wally install
Then require the package from the location configured by your project. Like for example:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local NumberSpinnerV2 = require(
ReplicatedStorage.Packages.NumberSpinnerV2
)
Manual installation
You can install the standalone ModuleScript manually by copying it from the GitHub repository or the uncopylocked showcase game.
Recommended structure:
ReplicatedStorage
└── Modules
└── NumberSpinnerV2
Then require it with:
local NumberSpinnerV2 = require(
ReplicatedStorage.Modules.NumberSpinnerV2
)
🔗 Links
- GitHub: NumberSpinnerV2
- Wally: NumberSpinnerV2
- Uncopylocked showcase game: NumberSpinnerV2 Showcase
made with ❤️ by biotoxin495
Package Details
Install command (Click to copy)
Version
1.2.2
License
MIT
Safe for commercial use
Automated license review — not legal advice.
