In this guide I will be showing you some good practices for graphical user interface (GUI) design, as well as how to make GUIs in Roblox.
Ultimately what you should do is up to you; I might have good advice, but there will always be exceptions. Design is a flexible discipline. Feel free to mess around or do things that are “wrong”. Just remember that you want to provide a good experience for your players, and a confusing or annoying UI will not help you in that goal.
I have reached the maximum character limit of 32000 characters on this guide (and there were already lots of replies by the time this happened) so I had to put the things not covered here (UI objects, ViewportFrames, tips) in a new topic: UI Design Objects & Tips
It is recommended that you read the current guide first if you are new to UI design in Roblox.
Size & Position
UDim2
Roblox GUI objects have all of their measurement done by a value type called UDim2
, which stands for Universal Dimension 2 (as in it is comprised of 2 UDim
values).
A UDim
value consists of two numbers: Scale
and Offset
. Scale
is a measurement based on the screen size - the number 1 would represent the entire size of the screen, and 0 would represent nothing. This also has compatibility for negatives and numbers larger/smaller than 1/-1. Offset
is simply a measurement of pixels.
Example of UDim2
A Frame being the horizontal size of its parent - minus 40 pixels - and being 30 pixels high.
Explanation of Scale and Offset
Scale is flexible and always refers to a portion of the screen size OR the size of the object’s parent GUI object. Scale is better when you need to have something that covers the whole screen, or when you need to position something in the center of the screen. In essence, Scale changes based on the same screen size.
Offset is essentially referring to pixels on the screen. This is better when you need pixel-perfect accuracy, such as displaying an image at maximum crispness or when you need to make something exactly 3 pixels away from the edge of the screen. In essence, Offset values will always stay the same, no matter the screen size.
Tip: Here are some interesting ways to use scale.
- Using
Scale
for the size property in theBillboardGui
object makes it resize automatically based on distance instead of staying a fixed size on your screen.- Using
Scale
to make health or progress bars. This makes it really easy for the scripter to code, as when calculating the size of the bar, the final result they need to get will stay the same no matter the actual size of the GUI (i.e. it will usually be a number from 0 to 1).
Using Size and Position
Note: The problems I will mention in this section are often why the UI you designed in Studio looks different in-game. When using Scale - or when, in fact, using anything at all - you will usually design to look good in a specific screen size which is usually the size you have the screen set to in studio. This often leads to elements not looking right when playing outside of studio.
Since I originally wrote this guide, my views on how to make good UIs in Roblox have changed. Originally I thought Offset should be used all the time except for when you specifically need something that scales (like a frame covering the entire screen) in which case Scale should be used.
The reason I had for why Scale shouldn’t be used was because it wasn’t easy to make objects using Scale keep their shape (more commonly known as aspect ratio, I will use this term from now on).
Example of scale being bad
Frame with Size {0.4, 0}, {0.5, 0}.
On a tablet, this frame looks like a square:
But at a different aspect ratio, like on a really wide screen, it suddenly becomes a big long rectangle:
Why is keeping aspect ratio important? Because many parts of your UI will need a similar aspect ratio to look good on different devices.
This is why originally I decided that Offset was better than scale – because the aspect ratio remains the same no matter what.
But as it turns out, Offset is not very good either, and for support on multiple devices will require redesigning your UI multiple times. When you size or position something using Offset, it won’t change anything when the screen size changes and so it becomes difficult to use the same UI on different devices.
Example of offset being bad
Frame with Size {0, 600}, {0, 600} and Position {0, 340}, {0, 60}
On a 1280 x 720 screen:
On an iPhone 7 (667 x 375) where you can only see the top left corner of the frame:
So it looks like both Scale and Offset kinda suck on their own. What do we do then? Well as it turns out, the core problem here is that we need our GUIs to maintain aspect ratio. And there are two great solutions to this Roblox has: the UIAspectRatioConstraint, and the SizeConstraint property of GUI objects. These will be covered later in the guide.
So now that we have solved the problem of aspect ratio, which one should you use? The answer in my case is Scale. I make my UIs 100% in Scale unless I have a specific need for offset. All three of my recent games have had only a single UI that scales perfectly across all devices, and with almost no Offset use at all.
AnchorPoint
All GUI objects also have a property called AnchorPoint
. This property is a simple Vector2
where each of its values range from 0 to 1 - similar to the Scale
value in a UDim
. The anchor point defines where the position will be applied to.
Currently, this frame has an AnchorPoint of 0,0
The frame is being positioned from the top left corner, so that anchor point will be where the frame’s Position property is.
Alternately, you could set the AnchorPoint to 1,1 so it will be positioned from the opposite side
Or, you could even change it to 0.5,0.5 so the object would be positioned from its center like Parts are
AnchorPoint
's main function is make to positioning GUIs easier and let you use less brainpower trying to calculate the position. This allows you to simply position an object at the bottom of the screen using a Position.Y.Scale
of 1
- as long as the Y
property of the AnchorPoint
property is set to 1
, the object will also be fully visible.
For example, AnchorPoint is set to 0,1 and Position is {0.3, 0}, {1, 0}
AnchorPoint
is a really useful, so you should definitely be using it in your UIs when it could make things easier.
ZIndex and DisplayOrder
There’s a property in every GUI object called ZIndex
that defines what is in front and behind of it. This defines whether an object is in front of another. A higher ZIndex is in front of a lower ZIndex.
The ZIndex property only affects objects within a single ScreenGui, so objects from two different ScreenGuis will always display separately regardless of ZIndex. Instead of the ZIndex property, ScreenGuis have a similar property called DisplayOrder. This determines whether the ScreenGui is in front or behind of other ScreenGuis.
Maintaining aspect ratio
As shown in the Using Size & Position section, maintaining aspect ratio is an important thing we often need to do in Roblox GUI design. There are two main methods for doing this.
SizeConstraint property
(Offset is unaffected by SizeConstraint, as it is based in pixels, so this only relevant for Scale.)
All GUI elements have a property called ‘SizeConstraint’ that lets you define what way an object’s size is calculated. There are 3 possible values for this property:
- RelativeXY
- RelativeXX
- RelativeYY
If we have RelativeXY, that means the X axis is sized according to the horizontal (X) size of the screen, and the Y axis is sized according to the vertical (Y) size of the screen.
If we have RelativeXX, that means both the X and Y axes use the horizontal (X) size of the screen. This means if you make a frame have the size {1, 0}, {1, 0} with this constraint, the frame will always be a square, no matter how tall the screen is, because its using the horizontal size to calculate the vertical size.
Frame sized {0.5, 0}, {0.5, 0} with RelativeXX
(Overflows outside of vertical screen space because half of width of screen is bigger than height of screen)
Conversely, if we have RelativeYY, that means both the X and Y axes use the vertical (Y) size of the screen. Same deal, it will always form a square because it uses the vertical size to calculate the horizontal size.
One thing you may have picked up is that if you’re using XX or YY, an object will always stay the same aspect ratio no matter what the screen size is. This is why it is useful.
Let’s imagine that you wanted to make a frame that is always twice as wide as it is tall. You could set the SizeConstraint to RelativeYY, then set the size to {2, 0}, {1, 0}. Yup, it’s that simple.
Now being more practical here, you want to make it 0.5 (half) of the screen’s height so that it doesn’t cover everything. The size required for this is {1, 0}, {0.5, 0}. What happened to the 2? The important part here is that to keep an aspect ratio, you need to always make things relative to the dominant axis (in this case, Y). So if you want width to be double the height, it should height * 2
, which in this case is 0.5 * 2 = 1
.
I usually use RelativeYY instead of RelativeXX, as in my experience the best scaling occurs when you base things off of the screen height and not the width. Most screens tend to be wider than they are tall, so often being within the vertical height of the screen will guarantee good scaling.
This is my preferred method of maintaining aspect ratio. It’s a lot quicker and less confusing to use than the UIAspectRatioConstraint. Using only this property, I can make an entire UI that scales very well across multiple different screen sizes and aspect ratios.
Tip: If you need objects to maintain aspect ratio inside a vertical ScrollingFrame, use RelativeXX. Otherwise basing their size off of the height will make the size get bigger as the CanvasSize increases, which is not a good thing. You can use the opposite (RelativeYY) for a horizontal ScrollingFrame.
UIAspectRatioConstraint
This object allows you set the aspect ratio of anything, including cells within layouts such as UIGridLayouts (if you put the UIAspectRatioConstraint inside the Layout object). You can read about it in some more depth here: UI Design Objects & Tips
Text
Scaling
All objects that have text also have a property called TextScaled
which is a bool of whether the text will resize itself to fit the size of the object is being applied to. It is useful if you are making text that scales with your UI, and also if you’re being lazy about the size of UI elements.
A common gripe of mine with text in Roblox is that people frequently use TextScaled without concern for what it actually looks like in the end. This often results with buttons in the same visual group having different text sizes, which just looks weird and inconsistent.
Here's some buttons with and without TextScaled being used badly
With TextScaled used badly
Without TextScaled being used (still ew – but better)
Colors
You may or may not have noticed that some colors at their fullest brightness still appear to be darker than other colors - for example, red or blue - and some colors are still bright.
In other words, "visually dark" colors like blue, purple and red are hard to see in front of a dark background.
But these lighter colors are easier to see
Note that the opposite of this is also true. Brighter colors like yellow and green are harder to see in front of a white background.
Due to this, you will want to consider what color your text is. A good rule of thumb: Use white text as much as possible (or other bright colors). It’s generally easier to see than dark colors, unless the rest of your game OR the GUIs are bright. If you really want to use a specific color, like blue, consider changing the lightness of the blue color, or adding extra things like stroke or a background.
Stroke
All text objects have two properties: TextStrokeTransparency
and TextStrokeColor3
. These are self-explanatory; one is the transparency of the stroke, the other is the color.
It is generally a good idea to have TextStrokeColor3 always contrast with the TextColor. If there isn’t contrast, the text starts to lose its defining shape and clarity, becomes harder to read, and doesn’t look that great (although in this case it can used as a glow effect).
Example
Alternatively, if a different stroke color is used, the text is clear and easy to read, looks pretty nice, and the text can also be read in front of any background as the stroke separates the text from the background.
Example
Stroke is very useful for white text as a transparency of 0.8 allows for that barely visible amount of shadow that distinguishes it from the background.
Background
An important thing to remember for text is what is behind it.
A cluttered background behind text can hide letters in a sea of varying color. The text becomes just another meaningless pattern.
Usually you’d want to keep what is behind the text fairly simple so people can easily read the complex symbols that make up the alphabet:
Not very good
Good, could be better
For comparison
You can see that the text with the blurred background is easier to see - this is because blurring essentially simplifies an image. It becomes less cluttered, and sharp shapes are now contrasting with smooth, fading shapes.
The text with the plain background is even easier to see - there is nothing behind it conflicting with the text.
Wrapping
Text wrapping occurs when text automatically creates a new line when it has reached the edge of the space it can fill. You see it in almost every word processor like Word and Google Docs, when you type too many words to fit in the screen it puts it on a new line.
This is an example of text wrapping
And this is an example without it
While it is incredibly useful for long bits of text in say, an essay, it is not as practical when using buttons. Sometimes it can make the text either go to a new line that can’t be seen or will change the text size when using TextScaled. Generally I think it’s a good idea to avoid wrapping text in buttons, either by resizing the button or making the text smaller or disabling TextWrapped, just so that all of the buttons have a similar appearance. This will all depend on your style of course.
Images
Images are very important as they open up a whole new range of custom shapes. The ROBLOX 2D engine is rather limited in the shapes it can provide, so uploading custom shapes is always helpful!
Often using unique images helps give your game identity and also can work with immersiveness. Making a cartoony game? Use big, round shapes and bold text!
Making a western game? Give all of your UI elements a wood grain texture!
Size
The size of an image can have a significant effect on what it looks like. Generally it’s not a good idea to use an image at a size bigger than what it was made for, e.g. if you have 10x10 image don’t use it at 100x100 because it will look pixelated and blurry.
Less importantly, images in Roblox will look the most crisp when used at their original resolution – so if you’ve got that 10x10 image, it will look the best when used at 10x10 size. Offset can come in handy to make sure an image is always at its original size. However, this crispness is not very important for most games as people don’t really care that much, and to use it you would need to support multiple screen sizes (which means if you wanted to do this, you would need a version of the image for all the different screen sizes.) I wouldn’t bother with this if I were you.
Black edges
Often when you upload an image to Roblox and then use it a size different than its original size, it will be given a subtle black outline. This can be annoying sometimes, although it is another minor detail. In my work I use the tool PixelFix to solve this efficiently: Pixelfix: Remove the black edges on scaled images
Here’s an article written by Quenty about this problem, why it occurs, and how you can fix it on your own: Fixing images in Roblox UI. This article is going to look how you… | by James Onnen (Quenty) | Roblox Development | Medium
9-Slicing
9-Slicing is a technique used to make images scale without the edges losing quality and shape.
For example, you can turn a 100 x 100 circle image into a rectangle with rounded corners by slicing it through the middle so that each of the four corners of the slice is one quarter of the circle.
100x100 cicle
Image with and without 9-slicing
So how do you do it?
9-Slicing slices an image up into nine rectangles. Four of these, the corners, don’t change in size at all. The edges, four of them again, only stretch on the side they on, so a horizontal edge will stretch horizontally but not vertically. This leaves a final part - the center. This will probably make up the majority of your frame, and is recommended to be a plain color so it can stretch freely without us having to worry about details being distorted or pixelated.
Each image object has a property called ScaleType
. Setting it to Slice
will reveal a new property called SliceCenter
. This revealed property is called a Rect
(take note scripters), and has two Vector2s, one for each corner of a rectangle. X0 and Y0 is the top left corner, X1 and Y1 is the bottom right corner. This property uses pixels as its unit, so you will need to know the original dimensions of the image before you can slice it.
You can find a more in-depth tutorial here: How to use SliceCenter (Roblox's 9-Slice GUI Property)
Spritesheets
Often you may want to make a button that you hover over have a special effect or an image be animated. You don’t want to upload lots of images for one simple thing… so that’s where spritesheets come in.
Spritesheet example
A spritesheet is made up of different sections all in the same size. It is useful for holding lots of images inside a single image. So how do you implement this in Roblox?
All image objects have two properties: ImageRectSize
and ImageRectOffset
, which are both of the Rect
value type (which short for rectangle, and contains two Vector2
values).
To define the size of each different sprite, you set ImageRectSize
. To change which sprite the image is currently ‘viewing’, you use ImageRectOffset
and set it to the location of the sprite (all in pixels of course).
Here’s an example:
I have two button images, one for the normal state, and one for when the button is hovered.
My 52x104 spritesheet
This is what the button and its properties look like in its normal state
Default view
And this is what the button and its properties look like when hovered (note how ImageRectOffset is 52px on the X axis, aka looking at the second image on the spritesheet)
Hover view
Padding
Padding (and margins) are something that a lot of people miss. Padding in design means a small empty space between the edges of elements. It’s kind of like visual zone with nothing in it, breathing room for the eyes. When a design doesn’t have padding, it looks kind of cut off and can feel like its overlapping with things that it isn’t.
You can see in this example that the edge of the text is touching the edge of the frame
Text
Most commonly this problem is found in objects with text, where the TextXAlignment
or TextYAlignment
is set to Left
/Right
or Up
/Down
. It doesn’t happen as much with Center
because the text has even, empty space on each side.
Alignment Left
Alignment Center
Unfortunately, Roblox has not given us functionality to add padding inside a single TextLabel. So the only real solution to this is to create different objects for the background frame and the text.
You can do this by putting the TextLabel inside the frame, centering the label using 0.5, 0.5 AnchorPoint and 0.5, 0.5 Scale position, and giving it a smaller size than the background frame.
Without padding
With padding
(Scale Y is 4x smaller than Scale X because the frame is 4x longer than it is tall)
Other Elements
While padding is used often with text, it is also relevant in other places as well. Depending on your style, it is good to separate things like buttons and frames from the edges of the parent object.
You’ve got two options when it comes to padding: you can do it manually, or you can use the UIPadding object or other layout objects (they have Padding properties). I mostly use the UIPadding object, although I manually create padding frequently as well.
Example of both types of padding being used in a frame with two TextLabels and a button
UIPadding was used to create the padding between the edge of the frame and the edge of elements inside it. Manual padding was used to keep all 3 elements separate from each other.
UIPadding
I will first introduce UIPadding as it is the simplest. You put the object inside the parent of all the objects you want it applied to, and then set the PaddingLeft, PaddingRight, PaddingTop, and PaddingBottom properties to get padding applied at those locations (these values are UDims, so is in the form {Scale, Offset}
).
You can see this in action using my example with 1/20th padding on each side
The cool thing about UIPadding is that when padding is applied, all child objects will be placed within the bounds of that padding, even if they have their size set to 1 in Scale.
In my example, all three of the elements have their horizontal size set to 1 in Scale, with no positioning information.
Note: The process is very similar for layout objects. However instead of defining padding for every corner, in layout objects you set the padding property either as a UDim2 (2 directions) or as a single UDim value (1 direction), where the direction is based on the layout. So if you’re using a UIListLayout, the padding is in the direction of the list.
Manual padding
Manual padding is essentially just manually resizing, repositioning and otherwise modifying elements so that they have space between them.
In the example above, each of the elements' vertical size and position was set so that there was empty space between each element. You can see that visualized here
There is also a slightly more complicated version used to replicate the behaviour the UIPadding has. For each element, you need to set the size to 1 - padding
and the position to padding
(or the center of the frame).
So for example, if you had a TextLabel inside a frame and you wanted 10px of padding on all sides, you could set the size to {1, -20}, {1, -20}
and the position to {0, 10}, {0, 10}
. Notice how the size is the full size of the parent, minus double the padding. The reason it is is double is because in this case I want to have padding on both the left and right sides. If we only wanted one side, we would only need to subtract 10 instead of 20.
Now for the position, there’s an easier way you can make the padding equal on all sides. By setting the AnchorPoint set 0.5, 0.5, you could set the position to {0.5, 0}, {0.5, 0}
and then it will always be placed in the center. This method is useful if you’re changing the padding frequently as it means you don’t need to update the position when the size updates.
General Design
A few final things to follow when designing in general:
-
Try to have some contrast between elements - firstly not having good contrast would make things hard to see for players, and secondly people with poor eyesight or colourblindness would have a very hard time (imagine: grey text on a grey background).
-
Try to keep your UI from intruding upon the game - if you have sidebar buttons that cover a third of the screen, that’s not a very good use of space. The same goes for anything else that can appear on the screen. Note that a large UI can give a cartoony effect, but don’t do overdo it.
-
Consistency! Your UI should have the same style in all of its elements.
If you’re going to use a free model or buy some scripted GUIs, it might be a good idea to edit the appearance of them so they fit the style of your game better. I’m not saying your UI should be uniform and only use a small number of colors (because that is boring and isn’t attractive to kids who like lots of bright colors), I’m just saying if possible you should pay some attention to how your UI works with itself.
Hopefully this was helpful
Don’t forget to check out UI Design Objects & Tips as well.
Good luck designing!