Candy Logo Copyright © 2025-2026 Candy Arts

Speech Display
Speech Display UI Elements display Spoken Lines on screen. Each Dialogue Mode uses its own: Their settings (and the Actor and Line overrides for them) are listed on each Dialogue Mode's page. Voice Player Every Speech Display UI Element has a voice player of its own, assigned to its voice_node variable. It plays the voice files and the Text-To-Speech of the Lines shown in that UI Element, and nothing else. There's no shared fallback: a UI Element with no voice_node plays no voice at all, and reports it when the game starts. See Voice Players for the node to use. Hook Functions The hook functions of a UI Element are in its script, an 'unsafe' file. Write yours in a copy of the script, attached to your own scenes, or updates will overwrite them (see Customizing UI Elements, on the UI page). Every Speech Display UI Element has these hook functions, called on the UI Element that shows the Line:
HookCalled
x_write_begun()When the spoken text starts to be written. With the typewriter off (writing_speed 0), right before the whole text appears.
x_write_progress()While the typewriter writes, each time characters appear.
x_write_finished()Once the whole text is written, before the Line waits for the player to advance.
x_speech_advance()When the Line is advanced, by the player or by auto-advance. Not called when the Dialogue is stopped while the Line waits.
candy_args contains "caller" (the Engine) and "node" (the UI Element). custom_data is [[line values], [Variation values]]: the Spoken Line's Custom Data, then that of the Variation being displayed. See UI Elements for how hook functions work. A global x_speech_advance() hook function is called right after the UI Element's, in every Dialogue Mode: see Hook Functions. Reacting to each character In x_write_progress(), a UI Element reacts as its text is written, for example by playing a typing sound or moving a portrait's mouth:
func x_write_progress(candy_args: Dictionary, user_args: Array, custom_data: Array): $TypingSound.play()
It's called once per frame at most, so a fast writing speed writes several characters per call. candy_args also contains:
  • "text_node": the RichTextLabel being written.
  • "added": the number of characters this call adds.
  • "written": the number of characters written so far.
  • "total": the number of characters the typewriter writes for this Line. The speaker's name and the end-of-line symbol aren't counted.
  • It isn't called for text that appears at once. That is the whole text when the typewriter is off, and the rest of a Line when the player skips its writing. x_write_finished() is called in both cases.
  • The Engine doesn't wait for it, so an await inside it never slows the writing down.
The voice player has hook functions of its own, called when voice playback starts: see Voice Players. Lexicon Clicks Speech Display UI Elements connect their text node's meta_clicked signal themselves, so clickable Lexicon keywords require no setup. Write what a click does in the _on_lexicon_clicked() function, not in the UI Element: see Lexicon. Writing Your Own Script Customizing a shipped scene only requires its script and required nodes. If you write a script of your own instead, the Engine expects these functions:
FunctionPurpose
display(engine, speech, custom_data)Displays a Line. speech (a CandySpeech) contains everything the Engine resolved for it: text, speaker, portrait, voice file, and every setting in speech.settings. The Line is over when this function returns: it must wait for writing, voice playback and the player's advance.
clear()Clears and hides the UI Element, and silences its voice player. Speech bubbles also receive the requesting Engine, as an engine argument: a bubble ignores the request when another Engine wrote its last Line, unless its master argument is true (UI_Clear's master clear). Called whenever the Engine needs it emptied: when the Dialogue Mode changes, when the Dialogue moves on with self_clear on, when the Dialogue ends, and by the UI_Clear command.
portrait_nodes()Optional: the portrait nodes to pause while the Dialogue is suspended.
The shipped scripts are the best starting point: each one contains the whole sequence for its UI Element, readable and modifiable in one place. Work in a copy of the script, under a new name: the shipped scripts are 'unsafe' files (see Customizing UI Elements, on the UI page).