Pardon our dust while we move our docs here. The Godot (GDScript) docs are on this site, and the Yarn language, Unity, Godot (C#) and Unreal docs are still at docs.yarnspinner.dev.

The Line Presenter and Options Presenter cover most games. If you want dialogue to look or behave differently, like speech bubbles over characters’ heads, a chat log, or a phone screen, you can write your own Presenter.

A Presenter is a script that extends YarnDialoguePresenter. The Dialogue Runner gives it each line and each set of options, and it decides how to show them.

How the Dialogue Runner uses Presenters

For each line, the Dialogue Runner calls run_line on every Presenter in its Presenters list, all at the same time. It moves on to the next line once every one of them has returned.

For each set of options, it calls run_options on every Presenter. The first one to return an option’s index decides which option was picked. The Dialogue Runner then tells the others to stop, and they return -1.

sequenceDiagram
  participant R as Dialogue Runner
  participant L as Line Presenter
  participant O as Options Presenter
  R->>L: run_line(line)
  R->>O: run_line(line)
  O-->>R: returns immediately
  L-->>R: returns once the player continues
  Note over R: Both have returned,<br>so the options start
  R->>L: run_options(options)
  R->>O: run_options(options)
  L-->>R: returns -1, it doesn't show options
  O-->>R: returns 1, the option the player picked
  Note over R: The dialogue carries on<br>after option 1
A line, then a set of options, with the Line Presenter and Options Presenter. The Dialogue Runner waits for both Presenters each time.

A Presenter has two jobs:

  • in run_line, show the line, wait for as long as it needs to, then tidy up and return,
  • in run_options, show the options, wait for the player to pick one, then return its index.

A simple Presenter

This Presenter shows each line in a Label, and each set of options as buttons. The player presses ui_accept, which is Enter, Space or a gamepad’s bottom face button, to move on to the next line.

class_name SimplePresenter
extends YarnDialoguePresenter

signal _option_chosen(index: int)

@export var label: Label
@export var options_box: VBoxContainer


func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("ui_accept"):
		dialogue_runner.request_next_line()


func run_line(
		line: YarnLine,
		token: YarnCancellationToken = null
) -> void:
	label.text = line.text
	await token.wait_for_next_content()
	label.text = ""


func run_options(
		options: Array[YarnOption],
		token: YarnCancellationToken = null
) -> int:
	for i in options.size():
		if not options[i].is_available:
			continue
		var button := Button.new()
		button.text = options[i].text
		button.pressed.connect(_option_chosen.emit.bind(i))
		options_box.add_child(button)

	var give_up := _option_chosen.emit.bind(-1)
	token.next_content_requested.connect(give_up)
	var index: int = await _option_chosen
	token.next_content_requested.disconnect(give_up)

	for button in options_box.get_children():
		button.queue_free()
	return index
Lines 1–2 Extend YarnDialoguePresenter. The class_name lets you find the Presenter by name in the Create New Node window.
Line 4 Emitted when the player picks an option, with its index. run_options waits for it.
Lines 6–7 The Label for lines, and the container for option buttons. Set both in the Inspector.
Lines 10–12 When the player presses ui_accept, ask the Dialogue Runner to move on. dialogue_runner is set for you when the Presenter is in a Dialogue Runner’s Presenters list.
Lines 15–18 run_line is called for each line. token tells you when the line should finish.
Line 19 Show the line. line.text includes the speaker’s name, like Rosa: Oh, hello.
Line 20 Wait until something asks for the next line, here the ui_accept press on line 12.
Line 21 Clear the label. When run_line returns, this Presenter is done with the line.
Lines 24–27 run_options is called for each set of options, and returns the index of the one the player picked.
Lines 28–34 Make a button for each option the player can choose. Pressing a button emits _option_chosen with that option’s index in the array.
Lines 36–37 If the Dialogue Runner stops the options, for example because another Presenter’s option was picked first or time ran out, emit -1, meaning no choice.
Lines 38–39 Wait for a choice, then stop listening to the token.
Lines 41–43 Remove the buttons, and return the index.
A Presenter that shows lines in a Label and options as buttons.

To use it:

  1. Build the nodes it needs: a Control with a VBoxContainer inside it, holding a Label and another VBoxContainer for the options.
GameNode2D
YarnDialogueRunner
CanvasLayer
SimplePresenterControl
VBoxContainer
Label
OptionsBoxVBoxContainer
A scene with the Presenter above as its only Presenter.
  1. Attach the script to the Control, and set its Label and Options Box in the Inspector.
The Inspector for SimplePresenter, with Label set to the Label node and Options Box set to OptionsBox
The Presenter's two exported properties, set to the nodes inside it.
  1. Add the Control to the Dialogue Runner’s Presenters list.
The Presenter above, running the Lighthouse conversation. Each line shows all at once, with the speaker's name in front, because this Presenter has no typewriter.

Showing lines

run_line gets the line as a YarnLine. The parts you’ll use most:

Property or functionWhat it is
line.textThe whole line, including the speaker’s name.
line.character_nameThe speaker’s name, or empty if the line has none.
line.text_without_character_nameThe line without the speaker’s name.
line.get_bbcode_text()The line without the speaker’s name, with its markup turned into BBCode, for a RichTextLabel.
line.line_idThe line’s ID, like line:rosa-hello.
line.metadataThe line’s hashtags.
What a Presenter can read from a line.

Follow these rules in run_line:

  • Wait for whatever takes time before you return: a typewriter, an animation, audio, or token.wait_for_next_content().
  • Return as soon as you can once the token asks for the next line. The Dialogue Runner can’t move on until you do.
  • If your Presenter has nothing to show for a line, return immediately.
  • Don’t call dialogue_runner.signal_content_complete(). The Dialogue Runner moves on by itself once every Presenter has returned.

The token

Each call to run_line and run_options gets a YarnCancellationToken. It tells your Presenter what the player or your game has requested:

Part of the tokenWhat it means
token.is_hurry_up_requestedThe player has asked for the line to finish showing now. Skip to the end of any typing or animation, but keep the line on screen.
token.is_next_content_requestedThe dialogue is moving on. Tidy up and return.
await token.wait_for_next_content()Waits until the dialogue moves on, or returns immediately if it already has.
token.hurry_up_requestedA signal, emitted when a hurry is requested.
token.next_content_requestedA signal, emitted when the dialogue moves on.
What a Presenter can check on the token.

These happen when:

  • the Line Presenter or a Line Advancer gets the player’s input,
  • your own code calls dialogue_runner.request_hurry_up() or dialogue_runner.request_next_line(),
  • another Presenter’s option is picked, or the Dialogue Runner’s Option Timeout runs out.

Showing options

run_options gets an array of YarnOption. Each has text, text_without_character_name and is_available, which is false when the option’s condition failed.

Return the index in the array of the option the player picked. Return -1 if your Presenter doesn’t handle options, or when the token says the dialogue is moving on.

Other functions you can use

If your Presenter has any of these functions, the Dialogue Runner calls them:

FunctionWhen it’s called
on_dialogue_started()Dialogue has started. Set up your UI here.
on_dialogue_completed()Dialogue has ended. Hide or clear your UI here.
on_node_started(node_name)A node has started running.
on_node_completed(node_name)A node has finished running.
prepare_for_lines(line_ids)Lines are about to run, with their IDs. Load anything they’ll need here, such as audio or images.
Functions the Dialogue Runner calls on a Presenter.

YarnDialoguePresenter provides two functions for showing and fading your Presenter:

  • _set_presenter_visible(visible) shows or hides the Presenter. If the Presenter is a Control or other CanvasItem, it shows or hides itself. Otherwise, it shows or hides the nodes directly under it.
  • await _fade_presenter_alpha(from, to, duration) fades the Presenter’s transparency from one value to another, from 0 to 1, over duration seconds.

Without writing a Presenter

If you prefer connecting signals to writing a script, add a YarnSignalPresenter to your scene and add it to the Presenters list. It emits a signal for each line and each set of options, and waits until you tell it to continue.

SignalWhen it’s emitted
line_received(line_data)A line has arrived. line_data is a Dictionary with text, character_name, line_id, metadata and bbcode_text.
options_received(options_data)Options have arrived. Each item has text, option_index, is_available, line_id and metadata.
presenter_dialogue_startedDialogue has started.
presenter_dialogue_completedDialogue has ended.
presenter_node_started(node_name)A node has started running.
presenter_node_completed(node_name)A node has finished running.
The Signal Presenter’s signals.

After a line, call proceed() on the Signal Presenter to move on. After options, call choose_option(index) with the option the player picked.

line_data.text doesn’t include the speaker’s name. The name is in line_data.character_name, which is empty for a line with no speaker.

This script does the same job as the simple Presenter above, using a Signal Presenter. It goes on any node in the scene, here the root:

extends Node2D

@export var signal_presenter: YarnSignalPresenter
@export var label: Label
@export var options_box: VBoxContainer


func _ready():
	signal_presenter.line_received.connect(_on_line_received)
	signal_presenter.options_received.connect(_on_options)


func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("ui_accept"):
		signal_presenter.proceed()


func _on_line_received(line_data: Dictionary) -> void:
	var speaker: String = line_data.character_name
	if speaker.is_empty():
		label.text = line_data.text
	else:
		label.text = speaker + ": " + line_data.text


func _on_options(options_data: Array) -> void:
	label.text = ""
	for option in options_data:
		if not option.is_available:
			continue
		var button := Button.new()
		button.text = option.text
		var index: int = option.option_index
		button.pressed.connect(_choose.bind(index))
		options_box.add_child(button)


func _choose(index: int) -> void:
	for button in options_box.get_children():
		button.queue_free()
	signal_presenter.choose_option(index)
Lines 3–5 The Signal Presenter, the Label for lines and the container for option buttons. Set all three in the Inspector.
Lines 8–10 Connect to the Signal Presenter’s signals when the scene starts.
Lines 13–15 When the player presses ui_accept, tell the Signal Presenter to move on to the next line.
Lines 18–23 Show each line, with the speaker’s name in front if it has one.
Lines 26–27 When options arrive, clear the line.
Lines 28–35 Make a button for each option the player can choose. Each button remembers its option’s option_index.
Lines 38–41 When a button is pressed, remove the buttons and tell the Signal Presenter which option was picked.
Showing lines and options by connecting to a Signal Presenter.
GameNode2D
YarnDialogueRunner
YarnSignalPresenter
CanvasLayer
VBoxContainer
Label
OptionsBoxVBoxContainer
The scene for the script above. The Signal Presenter is the only Presenter in the Dialogue Runner’s list.

Samples

These samples each have their own Presenter. Open them from the samples browser in the Yarn Spinner tab:

  • Welcome builds slides on a projector screen from lines of dialogue.
  • Background Chatter shows lines above the characters speaking them, and moves on without the player pressing anything.
  • Phone Chat shows lines and options as chat bubbles in a messaging app.
  • Options That Timeout shows a countdown bar under the options, and picks one when it runs out.
  • Themed Line Presenter extends the Line Presenter, with its own styles and fonts.
Next step A Different Kind of Presenter A second custom Presenter, to show how differently a Presenter can work.