Advertisement
UI & Interface Sounds

How to Integrate Dialogue Blips with Unity Text Animator (Without Building a Custom Dialogue System)

A practical integration guide for adding dialogue blips to Unity Text Animator, covering the built-in OnCharacterVisible event, the official TypeWriter Sounds example package, and the newer Dialogue Sounds Toolkit, with the specific settings that keep blips from becoming noise.

Text Animator for Unity already contains the mechanism that dialogue blips need. It is not a separate plugin, and it is not a custom script. It is the OnCharacterVisible event, which the plugin fires every time a character becomes visible during the typewriter effect, and which passes the revealed char as a parameter[reference:0]. A developer who subscribes to that event and plays a blip in the callback has implemented dialogue blips, without building a custom dialogue system or writing a character-by-character reveal loop.

This is different from the approach in the generic typewriter blip guide, which assumes the developer controls the reveal loop and can count characters manually. Text Animator owns the reveal loop. The integration is therefore a matter of hooking into an event that the plugin already provides, and configuring the sound playback so that it respects the rate at which the plugin reveals characters.

This article covers three integration paths, in order of complexity: the built-in OnCharacterVisible event with a minimal playback script, the official TypeWriter Sounds example package that the plugin ships with, and the newer Dialogue Sounds Toolkit for projects that need layered dialogue audio beyond simple blips. The blip design itself is covered in the square-wave synthesis guide and the genre-specific frequency guide.

What does the OnCharacterVisible event actually provide?

The event is defined on the typewriter component, which is added to the same GameObject as the TextAnimator component[reference:1]. It fires once per character as the text is revealed, and it passes the character itself as a char parameter. The documentation states explicitly that the event “passes a char as a parameter, so you can play different sounds based on different letters as well”[reference:2].

The event has one important edge case: it also fires on spaces. The documentation warns that “the event is triggered on spaces too, so play sounds according to the character type you prefer”[reference:3]. For blips, the practical consequence is that the callback should check whether the character is alphanumeric before playing a sound. A blip on a space sounds like a stutter, and the player will notice it immediately.

The typewriter component comes in two variants: TypewriterByCharacter, which reveals one letter at a time, and TypewriterByWord, which reveals word by word[reference:4]. The OnCharacterVisible event exists on both, but the practical behavior is different. With TypewriterByCharacter, the event fires once per letter and the blip rate is whatever the reveal speed dictates. With TypewriterByWord, the event may fire once per word or per character depending on the internal implementation, and the blip timing becomes less predictable. For dialogue blips, TypewriterByCharacter is the correct choice.

The reveal speed is controlled by the typewriter's settings, not by the blip code. A typewriter that reveals 30 characters per second will fire the event 30 times per second, which means a naive “play a blip on every event” implementation would produce 30 blips per second. The Web Audio blip tutorial describes why this is above the rate at which the ear can distinguish individual events. The callback needs to throttle itself.

How do you write the minimal integration script?

The script has three responsibilities: subscribe to the event, check whether the character should trigger a blip, and play the blip with a minimum delay between playbacks. The minimum delay is the parameter that prevents the blip rate from matching the character rate.

using Febucci.UI.Core;
using UnityEngine;

public class DialogueBlips : MonoBehaviour
{
    [SerializeField] TypewriterCore typewriter;
    [SerializeField] AudioSource blipSource;
    [SerializeField] AudioClip[] blipClips;
    [SerializeField] float minSoundDelay = 0.06f;

    private float lastBlipTime = -999f;

    void OnEnable()
    {
        typewriter.onCharacterVisible.AddListener(OnCharacterVisible);
    }

    void OnDisable()
    {
        typewriter.onCharacterVisible.RemoveListener(OnCharacterVisible);
    }

    void OnCharacterVisible(char c)
    {
        // Skip spaces and punctuation
        if (!char.IsLetterOrDigit(c)) return;

        // Throttle: enforce a minimum gap between blips
        if (Time.time - lastBlipTime < minSoundDelay) return;
        lastBlipTime = Time.time;

        // Play a random clip from the array
        if (blipClips.Length == 0) return;
        var clip = blipClips[Random.Range(0, blipClips.Length)];
        blipSource.PlayOneShot(clip);
    }
}

The minSoundDelay is set to 0.06 seconds in the example, which produces a maximum blip rate of about 16 blips per second. That is within the range that the ear can distinguish as separate events, and it is below the rate at which the blips start to merge into a continuous tone. The typewriter blip guide recommends a target rate of 8 to 15 blips per second; 0.06 seconds sits at the upper end of that range and is a reasonable default for a typewriter that reveals around 40 characters per second.

The character check is doing more work than it appears. char.IsLetterOrDigit returns false for spaces, punctuation, and most special characters. This means the blip fires only on letters and numbers, which is the behavior most dialogue systems use. A developer who wants punctuation to produce a different sound (a longer blip on a period, for example) can replace the check with a switch on the character type and play a different clip for each category.

The AudioSource.PlayOneShot call is the right playback method because it allows overlapping blips. If the typewriter reveals characters faster than the blip's duration, the previous blip is still playing when the next one starts, and PlayOneShot mixes them rather than interrupting. The mix hierarchy guide covers the bus routing that keeps the blips at a consistent level relative to the dialogue voice and the music.

What does the official TypeWriter Sounds package provide?

Text Animator ships with an example package called “TypeWriter Sounds” in the “Extra” folder. The documentation for the 2.x version describes it as a reference implementation that demonstrates the same pattern as the minimal script above, with four additional parameters that make it more configurable [reference:5].

Parameter Purpose
Source The AudioSource that plays the blips
Min Sound Delay Minimum time before another sound can play
Interrupt Previous Sound Whether a new blip stops the previous one
Random Sequence Random clip selection vs. sequential

The Min Sound Delay parameter is the same throttle as the minSoundDelay in the minimal script. The Interrupt Previous Sound parameter is the one that changes the character of the blip set: when true, the blips are tightly packed and each one cuts off the previous one, which produces a staccato, mechanical feel. When false, the blips overlap and the sound is smoother. For most dialogue, false is the right choice, because the overlapping blips merge into a continuous texture rather than a series of discrete ticks.

The Random Sequence parameter controls whether the clips are chosen randomly or played in order. Random is the default for dialogue blips, because the sequential order becomes predictable after the first cycle. The randomized blip variation guide covers the distribution question in detail; the practical setting for a dialogue system is random selection with a pool of at least four clips.

The example package is a starting point, not a complete solution. It does not handle per-character blips (the character parameter is ignored), it does not differentiate between letters and punctuation, and it does not provide a way to change the blip set based on the speaking character. Those features require either extending the example script or using the Dialogue Sounds Toolkit described below.

What does the Dialogue Sounds Toolkit add that the built-in package does not?

The Dialogue Sounds Toolkit is a separate asset created by the same developers as Text Animator. Its purpose is to handle the parts of dialogue audio that the built-in example package does not: per-character voices, layered effects, and the organization of a large sound library[reference:6].

The toolkit is not a replacement for Text Animator. It is designed to work alongside it, and its documentation states that it “works great together with Text Animator for Unity”[reference:7]. The integration is the same pattern as the minimal script: the toolkit subscribes to the typewriter's events and plays sounds based on what the typewriter reveals.

The features that justify the toolkit over the built-in package are specific. It provides 40 sounds across 8 categories with 400 variations in the base library [reference:8]. It supports Selector and Parallel clips, which allow layered dialogue audio (a blip plus a breath plus a room tone, for example) rather than a single clip per character[reference:9]. It includes per-character behavior, which means each speaking character can have a different blip set without separate scripts[reference:10]. And it provides ordered effect chains for reverb, EQ, pitch shifting, and other processing, which are applied at the dialogue audio level rather than in a DAW[reference:11].

The toolkit is $21 at the time of writing, discounted from $30[reference:12]. For a project that needs layered dialogue audio or per-character voices, the toolkit replaces what would otherwise be a custom implementation, and the cost is justified by the saved development time. For a project that needs simple blips on a single character, the built-in example package or the minimal script is sufficient, and the toolkit is not necessary.

How do you adjust the blip set when the text speed changes?

Text Animator allows the reveal speed to be changed at runtime, either through the typewriter's settings or through the player's text speed preference. A blip script that uses a fixed minSoundDelay will produce a different blip density when the text speed changes, because the character rate changes while the throttle does not.

The fix is to tie the minSoundDelay to the typewriter's current reveal speed. A text speed of 30 characters per second produces 30 events per second, so the blip throttle should be set to produce a target of 10 to 15 blips per second regardless of the text speed. The formula is straightforward: if the typewriter is revealing N characters per second and the target blip rate is 10, the throttle should allow a blip every N / 10 characters.

// In the callback, instead of a fixed time throttle:
private int characterCount = 0;
private int blipEveryNChars = 3; // adjusted based on text speed

void OnCharacterVisible(char c)
{
    if (!char.IsLetterOrDigit(c)) return;

    characterCount++;
    if (characterCount % blipEveryNChars != 0) return;

    // play blip
}

The blipEveryNChars value should be recalculated whenever the text speed changes. At the default text speed of 30 characters per second and a target of 10 blips per second, the value is 3. At a fast text speed of 60 characters per second, the value becomes 6. At a slow text speed of 15 characters per second, the value becomes 2 (rounded from 1.5, because a blip every character at 15 characters per second is still 15 blips per second, which is at the upper end of the acceptable range).

The per-character counting approach is the one that the typewriter blip guide recommends for any character-by-character reveal system. The time-based throttle is simpler to implement, but it does not adapt to text speed changes, and the player who changes the text speed will notice the blip density change even if they do not consciously identify it as a problem.

What happens when the dialogue also has voice acting?

Text Animator does not know whether the dialogue line has a voice track. The typewriter reveals characters regardless of whether there is a voice speaking, and the blip script will play blips regardless of whether there is a voice. The result is a dialogue system where a voiced line produces both the voice and the blips, which is usually the wrong sound.

The standard fix is to disable the blips when the voice track is present. The dialogue system that feeds text to Text Animator usually knows whether a line is voiced, and it can pass that information to the blip script. The script then checks a boolean before playing each blip, and skips playback when the line is voiced.

public bool IsVoiced { get; set; }

void OnCharacterVisible(char c)
{
    if (IsVoiced) return;
    // ... rest of the blip logic
}

The alternative is to keep the blips but reduce their volume during voiced lines. A reduction of 12 to 18 dB is the range that makes the blips present as a subtle texture rather than a competing layer. The ducking guide covers the sidechain approach that automates this reduction based on the voice track's level.

The choice between disabling and reducing depends on the game's aesthetic. A visual novel with full voice acting usually disables the blips, because the voice is doing the work that the blips would otherwise do. A game with partial voice acting (grunts and exclamations rather than full lines) keeps the blips at full volume and lets the voice and the blips coexist. The visual novel blip guide covers the specific considerations for the first case.

The integration between Text Animator and a dialogue system that knows about voice acting is straightforward, because Text Animator is not the system that owns the voice. The dialogue system owns both the text and the voice, and it can set the IsVoiced property on the blip script before calling typewriter.ShowText(textValue). The blip script does not need to know where the information came from; it only needs the boolean.

Text Animator provides the event that makes dialogue blips possible. The integration is not a custom dialogue system, and it is not a separate plugin. It is a subscription to OnCharacterVisible, a character check, and a throttle. The built-in example package and the Dialogue Sounds Toolkit are optional extensions for projects that need more than the minimal script, but the minimal script is enough to produce dialogue blips that match the quality of the rest of the UI audio, and it is the starting point that any Text Animator project should begin with.

Generate the Blip Clips for the Script

Open the SfxMaker blip generator and create a small set of four to six blips at the 1000–1400 Hz range, with a sine or triangle waveform and a 30–50 ms duration. These are the clips that the blipClips array in the minimal script will reference, and the set is large enough for random selection to sound varied without requiring a large library.

Open Blip Sound Generator →
Advertisement