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 Dialogue Runner has two helpers for getting lines ready:

  • The Line Provider gets each line’s text, in the right language, ready to show.
  • The Asset Provider finds files that go with each line, like images, and loads them before they’re needed.

The Line Provider

When a line runs, the Dialogue Runner gives the Line Provider the line’s ID, and gets back a line that’s ready for the Presenters:

flowchart TB
  id["Line ID: line:a1b2c3d"]
  presenters["Presenters"]
  id -->|"1. find the text"| text["Rosa: You've got {0} coins."]
  text -->|"2. fill in values"| subs["Rosa: You've got 15 coins."]
  subs -->|"3. read markup"| presenters
What the Line Provider does with each line.
  1. It finds the text in the current language, from Godot’s translations, or from your Yarn Script if there’s no translation. See Localising Your Game.
  2. It fills in values, like {$coins} or {days_since_lamp_lit()}, which the dialogue has already worked out.
  3. It reads the markup, like [b] or [wave], so the Presenters know what to show. See Markup.

In a translation, each value is written as a number in braces, like {0}. Translators should keep these, and can move them to wherever they belong in their language:

keys,en,de
YARN_line:c3d4e5f,Rosa: You've got {0} coins.,Rosa: Du hast {0} Münzen.
A line with $coins in it, exported and translated.

The Line Provider’s language settings are in the Dialogue Runner’s Line Provider property. See When a line isn’t translated. From GDScript, get it with dialogue_runner.get_line_provider().

Shadow lines

Sometimes the same line appears in more than one place, like a greeting that can come up in several nodes. A shadow line is a copy of another line that shares its translations and voice clip, so it only needs translating and recording once.

Give the copy a #shadow: tag with the ID of the line it copies, instead of a #line: tag:

title: Morning
---
Rosa: Hello again. #line:a1b2c3d
===

title: Evening
---
Rosa: Hello again. #shadow:a1b2c3d
===
Line 3 The original line.
Line 8 The shadow. The Line Provider looks up the original line’s translation for it, and the Voice-Over Presenter plays the original line’s clip.
A line, and a shadow of it in another node.

A shadow line must have exactly the same text as the line it copies. If it doesn’t, the Yarn Project won’t compile, and you’ll see “Shadow lines must have the same text as their source”.

The Asset Provider

The Dialogue Runner makes an Asset Provider when the scene starts. It isn’t in the Inspector. Get it from GDScript with dialogue_runner.get_asset_provider().

Its main use is finding a file for a line, by the line’s ID. It looks in two folders:

Kind of fileFolderFile types
Audiores://audio/dialogue/.ogg, .wav, .mp3
Imagesres://images/dialogue/.png, .jpg, .webp
Where the Asset Provider looks for a line’s files.

Name each file after its line’s ID, without the line: part. An image for the line tagged #line:a1b2c3d is res://images/dialogue/a1b2c3d.png. If the ID has a : or / after the line: part, replace it with _ in the file name.

To get a line’s file, call get_texture() for an image, or get_audio() for audio, with the line’s ID. Each returns null if there’s no file for the line.

This Presenter shows a picture for each line that has one:

extends YarnDialoguePresenter

@export var picture: TextureRect

func run_line(
		line: YarnLine,
		token: YarnCancellationToken = null
) -> void:
	var assets := dialogue_runner.get_asset_provider()
	picture.texture = assets.get_texture(line.line_id)
	await token.wait_for_next_content()
Line 10 The Dialogue Runner’s Asset Provider.
Line 11 The image named after this line’s ID, or null if there isn’t one, which clears the picture.
Line 12 Keep the picture up until the dialogue moves on.
A Presenter that shows an image for each line, from res://images/dialogue/.

The Voice-Over Presenter doesn’t use the Asset Provider. It finds its clips itself. See Voice-Over.

Loading files before they’re needed

Before a node’s lines run, the Dialogue Runner asks the Asset Provider to start loading their files in the background. By the time a line runs, its image or audio has usually loaded, so get_texture() and get_audio() don’t have to wait for the file.

Using your own file names

If your files aren’t named after line IDs, tell the Asset Provider which file belongs to which line. Do it before the dialogue starts:

var assets := dialogue_runner.get_asset_provider()
assets.register_asset("line:a1b2c3d", "res://portraits/rosa_happy.png")
assets.register_assets({
	"line:e5f6a7b": "res://portraits/rosa_sad.png",
	"line:c9d0e1f": "res://portraits/tom_confused.png",
})
Telling the Asset Provider about files with other names.

Or list them in a CSV file, and load it with load_mappings_from_csv(). The first row is headings, and it’s skipped. After that, each row has a line ID and a path:

line_id,path
line:a1b2c3d,res://portraits/rosa_happy.png
line:e5f6a7b,res://portraits/rosa_sad.png
portraits.csv, for load_mappings_from_csv().

A file you’ve registered is used for its line before the Asset Provider looks in its folders.

To look in different folders, set audio_base_path and image_base_path on the Asset Provider.

Choosing files with your own tag

Naming files after line IDs means the IDs have to stay the same. If you’d rather pick a line’s file yourself, give the line a tag of your own, like #picture:lamp, and read it in your Presenter. Tags are in the line’s metadata, without the #:

extends YarnDialoguePresenter
## Shows the picture named in a line's #picture: tag.

@export var picture: TextureRect

func run_line(
		line: YarnLine,
		token: YarnCancellationToken = null
) -> void:
	picture.texture = _picture_for(line)
	await token.wait_for_next_content()

func _picture_for(line: YarnLine) -> Texture2D:
	for tag in line.metadata:
		if tag.begins_with("picture:"):
			var file := tag.trim_prefix("picture:")
			return load("res://images/pictures/%s.png" % file)
	return null
Line 11 Show the line’s picture, or nothing if it doesn’t have one.
Lines 16–17 Look through the line’s tags for one that starts with picture:. #picture:lamp is in metadata as picture:lamp.
Lines 18–19 Load the file it names, from res://images/pictures/.
Line 20 No picture: tag, so no picture.
PicturePresenter.gd, a Presenter that shows the picture named in a line’s #picture: tag.

The lines keep their own line tags, so you can still use Add Line Tags to Yarn Scripts. Files loaded this way don’t go through the Asset Provider, so they aren’t loaded before they’re needed.

The demo scene

In this scene, Rosa is getting the player ready to climb the tower. She mentions the old lamp, the key, the back door and a map, and you want a picture of each one next to her line:

A lit lamp on a post lamp.png
A gold key key.png
A wooden door door.png
A paper map with a red route ending at an X map.png
The four pictures, in res://images/pictures/.

The writer tags each line with the picture that goes with it, like #picture:lamp, and this scene uses PicturePresenter from Choosing files with your own tag to show it. Rosa’s last line has no #picture: tag, so the frame is empty. Under the frame, a label shows which file each picture came from.

title: PicturesDemo
---
Rosa: This is the old lamp from the tower. #picture:lamp
Rosa: Here's the key to get in. #picture:key
Rosa: The door's round the back. #picture:door
Rosa: I'll draw you a map, so you don't get lost. #picture:map
Rosa: Good luck up there.
===
Lines 3–6 Each #picture: tag names a file in res://images/pictures/, without the .png.
Line 7 No #picture: tag, so the frame is empty.
The Yarn Script.
The demo scene running. Each line with a #picture: tag shows that picture.

The Dialogue Runner has two Presenters in its Presenters list, and it gives every line to both of them:

  • The Line Presenter shows Rosa’s text in the box at the bottom.
  • PicturePresenter looks through the line’s tags. For #picture:lamp, it loads res://images/pictures/lamp.png and puts it in the frame. For a line with no #picture: tag, it clears the frame.

Both Presenters then wait for the player. When the player presses continue, the token asks both of them for the next content. Once both return, the Dialogue Runner moves on to the next line. The label under the frame isn’t part of either Presenter. The scene’s own script checks which picture is showing, and shows the path of its file.

PicturesDemoControl
BackgroundColorRect
YarnDialogueRunner
VariableStorageYarnInMemoryVariableStorage
PicturePresenter
FramePanelContainer
PictureTextureRect
FileLabelLabel
YarnLinePresenter
The demo scene. PicturePresenter is in the Dialogue Runner’s Presenters list, next to the Line Presenter.
extends Control

@export var dialogue_runner: YarnDialogueRunner
@export var picture: TextureRect
@export var file_label: Label

func _ready():
	# Start again when the conversation ends.
	dialogue_runner.dialogue_completed.connect(
			_start, CONNECT_DEFERRED)
	_start()

func _start() -> void:
	dialogue_runner.start_dialogue("PicturesDemo")

func _process(_delta: float) -> void:
	# Show which file the picture came from.
	if picture.texture == null:
		file_label.text = "No image for this line"
	else:
		file_label.text = picture.texture.resource_path
Lines 3–5 Nodes in the scene, set in the Inspector.
Lines 9–16 Start the conversation, and start it again whenever it ends. The Dialogue Runner’s Auto Start is off.
Lines 19–24 Show the path of the picture’s file under the frame, or say there isn’t one.
The script on PicturesDemo.
Next step Samples Eighteen sample scenes, from a tour of the Yarn language to lip-synced voice-over in 3D.