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.

A Command is a line in a Yarn Script that tells your game to do something. It’s written inside << and >>, with the Command’s name first and any arguments after it:

title: CommandsExample
---
<<play_sound door_creak>>
Rosa: Oh! You gave me a fright.
<<move Rosa 700 400>>
<<wait 1>>
Rosa: Here, you'll need this.
<<give_item "brass key" 1>>
===
Three Commands in a node. The first word inside each is the Command’s name, and the rest are its arguments.

When the dialogue reaches a Command, the Dialogue Runner calls the GDScript method you’ve connected to it. Once the method has finished, the dialogue carries on.

sequenceDiagram
  participant R as Dialogue Runner
  participant A as Audio code
  participant Rosa as Rosa (Character.gd)
  Note over R: #60;#60;play_sound door_creak#62;#62;
  R->>A: _yarn_command_play_sound("door_creak")
  A-->>R: returns
  Note over R: Shows "Oh! You gave me a fright."
  Note over R: #60;#60;move Rosa 700 400#62;#62;
  R->>Rosa: _yarn_command_move(700, 400)
  Rosa-->>R: returns a Signal
  Note over Rosa: Rosa moves for 1 second
  Rosa-->>R: the Signal is emitted
  Note over R: Carries on with #60;#60;wait 1#62;#62;
The first two Commands from the node above. The Dialogue Runner calls a method for each one. The move method returns a Signal, so the dialogue waits until Rosa has finished moving.

Writing a Command

The quickest way to make a Command is to give a method a name that starts with _yarn_command_. The rest of the name is the Command’s name in Yarn, so _yarn_command_play_sound is <<play_sound>>.

When the Dialogue Runner starts, it looks for these methods in:

  • scripts with a class_name, anywhere in your project,
  • scripts attached to nodes in the running scene,
  • autoloads.

If nodes are added to the scene later, it searches again the first time it reaches a Command it doesn’t know.

There are two kinds of Command, depending on whether the method is static.

Commands that aren’t tied to a node

A static method becomes a Command you can use anywhere:

static func _yarn_command_play_sound(sound_name: String) -> void:
	AudioManager.play(sound_name)
A static Command. «play_sound door_creak» calls it with sound_name set to door_creak.
<<play_sound door_creak>>
Using the static Command in Yarn.

A method in an autoload works the same way, static or not. Autoloads suit Commands that the whole game uses.

Commands on a node

A method that isn’t static belongs to a node. In Yarn, the first argument is the name of the node to call it on:

extends Node2D


func _yarn_command_move(x: float, y: float) -> Signal:
	var tween := create_tween()
	tween.tween_property(self, "position", Vector2(x, y), 1.0)
	return tween.finished
Line 1 The script goes on the character’s node, which is called Rosa in the scene.
Line 4 It isn’t static, so it’s a Command on a node. In Yarn, the first argument, Rosa, says which node to call it on. x and y come from the other two arguments, converted to float.
Lines 5–6 Move the node to the new position over one second.
Line 7 Return the tween’s finished signal, so the dialogue waits until the move is done.
Character.gd, attached to a node called Rosa. «move Rosa 700 400» calls it on Rosa.
<<move Rosa 700 400>>
Calling the Command on the node called Rosa.

If every character in your game uses the same script, each one gets a <<move>> Command, and the first argument says which character moves. The Dialogue Runner looks for a node with that name anywhere in the running game. If it can’t find one, or the node doesn’t have the script, it logs an error and the dialogue carries on.

Arguments

Arguments are separated by spaces. To pass an argument with a space in it, put it in double quotes: <<give_item "brass key" 1>>.

Each argument is converted to the type of the method’s parameter:

Parameter typeWhat you write in Yarn
StringAny text.
int, floatA number, like 3 or 0.5.
booltrue or false. Writing the parameter’s name means true: for a parameter called wait, <<fade wait>> passes true.
Vector2, Vector3Numbers separated by commas, with no spaces, like 4,2.
ColorA hex code like #ff8800, or a colour name like orange.
A node type, like Node2DThe name of a node in the scene. The Dialogue Runner finds it and passes it in. If there’s no such node, it passes null and logs a warning.
How Command arguments are converted to each parameter type.

A parameter without a type gets the argument as a String. Parameters with default values can be left out.

If there are too few or too many arguments, or one can’t be converted, the Dialogue Runner logs an error saying which one, doesn’t call the method, and carries on with the dialogue.

To use a variable’s value as an argument, put it in curly braces: <<give_item {$reward} 1>>.

Making the dialogue wait

The dialogue carries on as soon as your method returns. To make it wait until something has finished, like an animation or a sound, do one of these:

  • Return a Signal. The dialogue waits until it’s emitted. The move Command above returns its tween’s finished signal, so the next line only appears once Rosa has arrived.
  • Use await in the method. The dialogue waits until the method has finished.
func _yarn_command_wave() -> void:
	$AnimationPlayer.play("wave")
	await $AnimationPlayer.animation_finished
A Command that waits for an animation, using await.

To wait with a timer, use YarnAsync.wait(self, seconds) rather than get_tree().create_timer(). It stops counting while the game is paused. See Async and Cancellation.

Adding Commands from code

You can add a Command to a Dialogue Runner yourself, with any name and any method:

@export var dialogue_runner: YarnDialogueRunner


func _ready():
	dialogue_runner.add_command("give_item", _give_item)


func _give_item(item_name: String, count: int) -> void:
	Inventory.add(item_name, count)
Line 1 The Dialogue Runner, set in the Inspector.
Lines 4–5 Add the Command when the scene starts. The first argument is its name in Yarn.
Line 8 A named method with typed parameters, so <<give_item "brass key" 1>> passes a String and an int.
Line 9 Your game’s own code. Inventory stands in for however your game keeps track of items.
Adding a «give_item» Command when the scene starts.

Pass a named method, like _give_item, rather than a lambda. The Dialogue Runner converts arguments using the method’s parameter types, and a lambda has none, so it gets every argument as a String.

To remove a Command, call dialogue_runner.remove_command("give_item").

To add a Command to every Dialogue Runner in your game, call YarnSpinner.register_command() with the same arguments. YarnSpinner is an autoload that the addon adds when it’s enabled. Each Dialogue Runner picks up these Commands when it starts, so register them early, for example in your own autoload’s _ready.

Where the Dialogue Runner looks

The Dialogue Runner’s Auto-Discovery settings control how it finds _yarn_command_ methods:

SettingDefaultWhat it does
Auto Discover CommandsOnFinds _yarn_command_ and _yarn_function_ methods by itself. Turn it off to only use Commands you add yourself.
Discovery RootemptyThe node to look under for scripts attached to nodes. When it’s empty, the Dialogue Runner looks through the whole running scene.
The Dialogue Runner’s Auto-Discovery settings.

Scripts with a class_name, and autoloads, are found wherever they are. A script without a class_name is only found if it’s attached to a node under the Discovery Root.

Two Commands can’t have the same name. If a second method uses a name that’s already taken, the Dialogue Runner logs an error and ignores it.

Built-in Commands

Two Commands are always available:

CommandWhat it does
<<wait 2>>Waits for the number of seconds you give it before the dialogue carries on. It stops counting while the game is paused.
<<stop>>Ends the dialogue immediately.
Commands that are always available.

When a Command isn’t found

If the dialogue reaches a Command that isn’t connected to anything, the Dialogue Runner logs an error and carries on with the dialogue.

To handle those Commands yourself, connect to the Dialogue Runner’s command_unhandled signal. It passes the whole Command as text, like play_sound door_creak, and the error isn’t logged. The dialogue then waits until you call dialogue_runner.signal_content_complete(), so call it once you’ve dealt with the Command.

Before it runs any Command, the Dialogue Runner emits command_received with the Command’s name and its arguments. Use it to log Commands, or to react to them in more than one place.

Autocomplete in VS Code

The Yarn Spinner extension for VS Code can suggest your Commands as you type, and warn you about ones it doesn’t know. It reads them from the project’s .ysls.json file, which the addon writes for you. See The .ysls.json file.

The demo scene

This scene uses each kind of Command. Rosa hears the door creak, walks over to the lamp and lights it, and gives the player a key. The panel in the top left lists each Command as the dialogue reaches it.

The demo scene running. Each Command changes something on screen, and the panel in the top left lists them in order.
GameNode2D
BackgroundColorRect
FloorColorRect
DoorColorRect
LampColorRect
GlowColorRect
RosaNode2D
BodyColorRect
NameLabel
YarnDialogueRunner
UICanvasLayer
CallLogPanelPanelContainer
CallLogRichTextLabel
InventoryPanelPanelContainer
InventoryLabel
YarnLinePresenter
The demo scene. Game’s script adds the Commands, and Rosa’s script has the move Command.
title: CommandsDemo
---
<<play_sound door_creak>>
Rosa: Oh! You gave me a fright.
<<move Rosa 640 430>>
Rosa: Let me get the lamp going again.
<<light_lamp>>
Rosa: There. Much better.
Rosa: You'll need this to get up the stairs.
<<give_item "brass key">>
Rosa: Up you go!
===
The Yarn Script.
extends Node2D

@export var dialogue_runner: YarnDialogueRunner
@export var call_log: RichTextLabel
@export var inventory_label: Label
@export var door: Control
@export var lamp: ColorRect
@export var glow: ColorRect

var _inventory: Array[String] = []


func _ready():
	dialogue_runner.add_command("play_sound", _play_sound)
	dialogue_runner.add_command("light_lamp", _light_lamp)
	dialogue_runner.add_command("give_item", _give_item)
	dialogue_runner.command_received.connect(_log_command)
	_update_inventory()


func _log_command(command_name: String, args: Array) -> void:
	var text := command_name
	for arg in args:
		var value := str(arg)
		text += " " + ('"%s"' % value if " " in value else value)
	call_log.append_text("[color=#e6a15a]Command[/color]  <<%s>>\n" % text)


# Commands

func _play_sound(sound_name: String) -> void:
	var label := Label.new()
	label.text = "*creak!*" if sound_name == "door_creak" else "*" + sound_name + "*"
	label.add_theme_font_size_override("font_size", 32)
	label.position = door.position + Vector2(-10, -50)
	add_child(label)
	var tween := create_tween()
	tween.tween_property(label, "position:y", label.position.y - 60, 1.2)
	tween.parallel().tween_property(label, "modulate:a", 0.0, 1.2)
	tween.tween_callback(label.queue_free)


func _light_lamp() -> Signal:
	var tween := create_tween()
	tween.tween_property(lamp, "color", Color(1.0, 0.85, 0.35), 0.8)
	tween.parallel().tween_property(glow, "modulate:a", 1.0, 0.8)
	return tween.finished


func _give_item(item_name: String) -> void:
	_inventory.append(item_name)
	_update_inventory()


func _update_inventory() -> void:
	var text := "Inventory\n"
	if _inventory.is_empty():
		text += "(empty)"
	for item in _inventory:
		text += "• " + item + "\n"
	inventory_label.text = text
Lines 3–8 Nodes in the scene, set in the Inspector.
Line 10 The inventory that <<give_item>> adds to.
Lines 14–16 Add three Commands. <<move>> isn’t here: it’s on Rosa’s script, and the Dialogue Runner finds it by itself.
Line 17 Call _log_command for every Command, before it runs.
Lines 21–26 Write each Command to the panel in the top left, with quotes around arguments that have spaces. This is only for the demo.
Lines 31–40 <<play_sound>>. There’s no real sound: it shows “creak!” by the door, then floats it up and fades it out. It returns nothing, so the dialogue doesn’t wait.
Lines 43–47 <<light_lamp>>. It returns the tween’s finished signal, so the dialogue waits for the lamp.
Lines 50–52 <<give_item>>. It adds the item and redraws the inventory.
Lines 55–61 Show the inventory in the top right.
The script on the Game node. It adds three Commands in _ready. _log_command writes each Command to the panel in the top left.
extends Node2D


func _yarn_command_move(x: float, y: float) -> Signal:
	var tween := create_tween()
	tween.tween_property(self, "position", Vector2(x, y), 1.0)
	return tween.finished
Character.gd, on Rosa. The Dialogue Runner finds _yarn_command_move by itself.
Next step Functions Getting values from your game to use in lines and conditions.