How to Dynamically Scale Font Size in Godot [Clean Approach]

How to Dynamically Scale Font Size in Godot [Clean Approach]

Godot 4 doesn't auto-scale font sizes when your UI controls resize, which means text can look wrong at different resolutions. We ran into this while working on our game and wrote a simple GDScript solution that adjusts font sizes through theme overrides at runtime, no pixelation, no hacks. This tutorial covers the full approach for both Label and RichTextLabel nodes.

How do I dynamically change font size in Godot?

In the difficult quest of making games compatible with (almost) all the existing screen resolutions, one of the main problem we've encountered is how to scale texts properly when resizing the UI.

In Godot (at least up to version 4.4) text size is not automatically scaled if its control scale changes (e.g. Label, RichTextLabel, etc...), so we need some tricks to get around this problem. If you're new to Godot UI in general, check out our Godot UI tutorial first for the core concepts.

0:00
/0:02

Our goal target

We're not thinking about resizing the windows!

You might go to Project Settings -> Window -> Strech Mode and then select something like the canvas_item mode (which would scale the entire window). It can definitely work, but it's not for every game or scenario (like ours, where we have some content that is scaling while the window size is the same or scales differently), so we need a proper "scaler" solution! (I also miss Unity's TextMeshPro "Best Fit" option, which fits the entire text inside the control 🥲)

"Font scaler" script

We need to create a script that automatically scales the font of a target text node (e.g. Label, RichTextLabel, etc...). The full solution for a Label node comes in at around 25 lines of GDScript, and the RichTextLabel version (which handles 5 separate font size themes) adds about 15 more.

Initial state

We are scaling the font/text based on the initial state of our label, which means that we need to cache everything from our target text first. This includes the initial font size and the control size itself (so that we can make a linear proportion).

💡
PS. This example is applied to the "Label" node (which has only one font size). We have included a more complex version for the RichTextLabel later in this article, including the full code.
var base_font_size : int
var base_size : Vector2
var label : Label

func _enter_tree():
  # caches initial sizes
  label = get_parent() as Label
  base_size = label.size
  base_font_size = label.get_theme_font_size("font_size", label.get_class())

GDScript snippet to initialize the base size

Updating the font when the control changes

Every time the control is scaled we need to compare the new size with the base one, calculate the scaling ratio, and update the font size. There is also a hard cap at 4096 pixels for bitmap fonts, so we need to guard against that too.

  1. Listen to the resized signal in _enter_tree()
  2. Disconnect from it in _exit_tree()
  3. In the handler, compute scale = new_size.x / base_size.x
  4. Multiply base_font_size * scale, clamp to 4096, then call add_theme_font_size_override

First we need to listen to whenever the control is resized. We're using the resized signal here, and if you want to go deeper on how signals work in Godot, we wrote a full post on Godot signals architecture.

func _enter_tree():
  # ...

  label.resized.connect(set_text_size)
  
  # ...

func _exit_tree():
  # remember to disconnect when exiting!
  label.resized.disconnect(set_text_size)

Then, the actual method that applies the scale:

func set_text_size():
	var new_size = label.size
	
	# scale base on control width
	var scale = new_size.x / base_size.x
	var scaled_size :int= floor(base_font_size * scale)

	# bitmap cannot be greater than 4096
	if scaled_size>4096:
		return
	
	# apply scale
	label.add_theme_font_size_override("font_size", scaled_size)

Now the Label text should automatically scale when the text control changes size. Yay!


Modifying the code for RichTextLabels

RichTextLabels are a bit more complex, because they have multiple font sizes inside their theme (for the "normal" font, italic, bold etc.). Specifically, RichTextLabel exposes 5 font size theme overrides: normal_font_size, bold_font_size, italics_font_size, bold_italics_font_size, and mono_font_size.

The rest of this post is available for free!

Please create an account to continue reading! It helps us knowing that humans are reading us, and also helps us staying independent and keep posting what we love. Thanks!

Create a Free Account & Continue Reading Already have an account? Log in

Want game-ready tools or need specific services and consulting?

Work with us