Qamico

How to create a Typewriter Text effect in Unity

When presenting dialogue in Unity, it’s possible to reveal text gradually, one character at a time, instead of displaying the full sentence instantly.

This type of “typewriter” effect can make dialogue feel more alive and paced, almost like it’s being spoken rather than simply shown. Instead of reading a finished line, the player experiences it unfolding in real time.

But how is it actually implemented?

The simplest approach is to build the string incrementally, appending one character at a time to a text field, with a short delay between each step. In Unity, this is commonly done using a Coroutine combined with a loop and WaitForSeconds.

Like this:

float charactersPerSecond = 5;

IEnumerator TypeText(string line)
{
    string textBuffer = null;

    foreach (char c in line)
    {
        textBuffer += c;
        dialogueText.text = textBuffer;
        yield return new WaitForSeconds(1 / charactersPerSecond);
    }
}

This works well for basic use, but it has a subtle limitation: it is tied to frame timing. Because it waits per iteration, the perceived speed can vary depending on frame rate and update consistency. In practice, this means the effect is most reliable when the typing speed is slower than or equal to one character per frame.

For most dialogue systems, that’s usually fine. But if you want faster text display, or if you need consistent timing regardless of performance fluctuations, a more robust approach is needed.

A better solution is to decouple character timing from frame updates entirely by tracking elapsed time manually and only advancing the text when enough time has accumulated.

This creates a system that remains stable even if the frame rate drops or spikes.

Here’s an example:

float charactersPerSecond = 90;

IEnumerator TypeTextUncapped(string line)
{
    float timer = 0;
    float interval = 1f / charactersPerSecond;
    string textBuffer = null;
    char[] chars = line.ToCharArray();
    int i = 0;

    while (i < chars.Length)
    {
        if (timer < Time.deltaTime)
        {
            textBuffer += chars[i];
            dialogueText.text = textBuffer;
            timer += interval;
            i++;
        }
        else
        {
            timer -= Time.deltaTime;
            yield return null;
        }
    }
}

In this version, the system doesn’t simply “wait” between characters. Instead, it accumulates time and determines how many characters should be displayed based on elapsed time. If multiple characters should appear within a single frame, they can be processed together, keeping the effect consistent and smooth.

This makes the behaviour independent of frame rate in practice, because timing is governed by accumulated deltas rather than per-frame waits.

Conceptually, it’s closer to how Unity’s physics loop works than a simple coroutine delay: instead of doing one fixed action per frame, it ensures the correct number of actions are applied over time, even if updates arrive unevenly.

The result is a typewriter system that feels stable, scalable, and suitable for everything from slow, cinematic dialogue to rapid text bursts without losing timing accuracy.