New UI backend going live soon

For almost a year, we’ve been working on a rewrite of the system which handles the sizing and positioning of UI elements. Here’s a few of the reasons why:

  • The new system is 2x faster at recalculating the size and position of UI objects, such as when scrolling. Parenting UI objects into a hierarchy is significantly faster now as well.
  • Properties like AbsoluteContentSize and AbsolutePosition are now up to date when you read them from Lua, instead of being stale until the end of the frame.
  • Many bugs that were almost impossible to fix in the old system are fixed in the new system.
  • We made the new UI system possible to unit test (the old one wasn’t), and wrote a suite of unit tests to make sure we don’t ship regressions.
  • We want to ship fewer UI bugs and more UI features going forward.
  • We want to eventually add a feature to size GUIs based on their contents, which was impossible in the old system.

Release plans

We’ll try to turn on the new system for the first time next week. We may have to revert it, and we may turn it on or off for specific games. If you run into any issues, please post on the dev forums or private message me so that we can fix them.

If you haven’t already, you may want to create a test place using a copy of your existing games and get yourself added to the beta available here:

We’ve already done a lot of testing internally - we extensively tested the top 50 games and have so far fixed over 40 issues caused by the new code.


Your UIs should look the same under the new system. We’ve been very careful to ensure this - we’ve even gone out of our way to reimplement certain behaviors that we consider to be bugs (bug compatibility), because we found several games were relying on them.

The majority of real-world UI code is unaffected. However, the new system behaves differently in how property changes work compared to the old system, which means that some code patterns will no longer behave the same.

In the old system, when an object is not influenced by a UILayout, changing Size or Position will cause AbsoluteSize or AbsolutePosition to fire immediately - the code that set the Size won’t return until the changed event handler has run.

In the old system, when an object is influenced by a UILayout, changes that can cause AbsolutePosition to change, like changing the Size of another object in the layout, will not cause AbsolutePosition to fire until the end of the frame, meaning that the value will be out of date when read by Lua until the end of the frame.

With the new system, it doesn’t matter whether an object is influenced by a UILayout: In both cases, AbsoluteSize and AbsolutePosition will not fire until the value is observed. Observing can happen in one of two ways: Reading the property from Lua, or by the UI being rendered to the screen at the end of the frame. This means that reading a property from Lua can trigger a changed event to fire before returning to the code that requested the value.

Specific patterns

This isn’t an exhaustive list, these are just some patterns we found that break.

Expecting changed events to fire immediately while setting properties

local SomeGui = ... -- a TextLabel in this example
local HeldLock = false

local function Update()
    HeldLock = true
    SomeGui.Size =, 0, 0, 1000)

    local bounds = SomeGui.TextBounds

    SomeGui.Size =, 0, 0, bounds.Y) -- In the old system, changed event would fire here.
    HeldLock = false
    -- In the new system, it will fire at the end of the frame, long after releasing the HeldLock
    -- if we added a line like this before setting HeldLock to false:
    -- local _ = SomeGui.AbsolutePosition
    -- Then the changed event would be triggered on that line in the new system. Please don't do this, though.

    if not HeldLock then

This is similar to a snippet we found in the in-game chat code. If you’ve forked the in-game chat, you should update your fork to get the fix we did for this.

This exact snippet is an easy fix: Just use TextService:GetTextSize() instead of TextBounds.

What happened with this code is that, because AbsoluteSize doesn’t fire immediately during setting the Size property, HeldLock would be set to false, and the Changed event would fire at the end of the frame because it wasn’t observed in any other way. This means that at the end of the frame, the changed handler will run and then cause the update function to run all over again - causing the Update() function to run every frame when it wouldn’t have before.

Certain ways of sizing GUIs to children

Most developers don’t do this, and the few that do have wildly varying implementations.Most simpler implementations - like simply setting the CanvasSize based on AbsoluteContentSize - should have no problem.

Some more complex implementations rely on behavior that changes under the new system. If you’ve written something like this, you may want to check to see if your code will be affected.


  • The internal codename of the new system is “Quantum Gui”, because the layout state is undefined until you observe it.
  • I’m the main engineer that worked on this.
  • If you know C++ and have experience with systems like this, we’re hiring!

Well, my day just got made. Thank you very much!

  • The internal codename of the new system is “Quantum Gui”, because the layout state is undefined until you observe it.

>slaps knee


Yes, along with the other 41 positions. Good luck

1 Like

Looking forward to this rewritten backend. There are some UI performance tests I’m interested in doing with them, and I am hyped for the possibility of new UI features from now on. Great work!


We’re currently planning to enable the new system for PC and macOS on Wednesday, the 26th. Mobile and Xbox will come later.


Will this new backend include decimal font sizes by any chance?


No, that’s a separate feature that we’re looking into.


Is there a fast flag to turn on this behavior? I’d like to develop with it in a local studio session, as I’m creating hacky workarounds at the moment.

I sent a PM with information and a disclaimer.

1 Like

As of now, the new system is live for PC (Win32, not UWP) and mac.

Edit: We’ve had to turn it off because of an increased crash rate. We’ll probably be trying again next week.


We’re planning on turning the new system on again for PC (Win32, macOS, not UWP) on Thursday, some time between 11 AM and 4 PM PT.

The only issue last time was 3 different crashes, one of which caused a 100% crash rate for two top-10000 games. These are now fixed.


The new system is live for PC only as of 11:31 AM PDT.

Edit: Disabled at 1:49 PM PDT due to builtin plugins in Studio relying on bad behaviors.


We’re planning on making a third attempt to turn on the new system for PC and macOS tomorrow (Thursday, October 25). We’ll be doing it mid-day like previous attempts.

Last time, the main issues which came up were builtin plugins being broken. A few games had some issues, but we’ve either fixed them or added their places to a temporary blacklist.


The new system is enabled for PC as of 11:37 AM PDT.


May just be me, but scrolling frames seem to be bugged


1 Like

I’m not completely sure if it’s related, but there is a good chance it might be. So, recently I started to experience this bug with UIScale (Scale property). I have more details and a repro here: UIScale broken when Scale ~= 1

I believe this is related to the beta, but the chat UI is huge, and stretches all the way to the ground.


This could be the culprit, try this.

This is caused by a bug in the chat scripts which we fixed a few weeks ago. You’ll need to update the ChatBar script in order to get the fix. There’s also a manual patch you can do if that’s not an option.

1 Like