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 Function lets a Yarn Script ask your game for a value, like how many coins the player has, or whether they’re carrying a key. You call it by name, with any arguments in brackets, and your game sends back the answer:

title: FunctionsDemo
---
<<if has_item("brass key")>>
    Rosa: Good, you've got the key.
<<else>>
    Rosa: You'll need a key to get up the stairs.
<<endif>>
<<set $coin_total to coin_count()>>
Rosa: You've got {$coin_total} coins.
===
Two Functions in a node. has_item decides which line Rosa says, and coin_count’s value is shown in a line.

Where a Command tells your game to do something, a Function asks it a question. The dialogue uses the answer and carries on.

sequenceDiagram
  participant R as Dialogue Runner
  participant G as Your game code
  Note over R: #60;#60;if has_item("brass key")#62;#62;
  R->>G: _has_item("brass key")
  G-->>R: returns false
  Note over R: Shows "You'll need a key to get up the stairs."
  Note over R: #60;#60;set $coin_total to coin_count()#62;#62;
  R->>G: _coin_count()
  G-->>R: returns 15
  Note over R: Shows "You've got 15 coins."
The Dialogue Runner calls the Function’s method in your game, and the value it returns is used in the Yarn Script.
Functions in a scene. The panel in the top left lists each Function call and the value it returned, and Rosa's lines use those values.

Using Functions in Yarn

You can use a Function anywhere Yarn expects a value:

  • in a condition: <<if has_item("brass key")>>,
  • in a <<set>>: <<set $coin_total to coin_count()>>,
  • in an expression with other values: <<if coin_count() >= 10>>.

Put text arguments in double quotes, like "brass key". Numbers, true and false, and variables go in as they are: has_item($chosen_item).

A Function can go directly inside a line, like Rosa: You've got {coin_count()} coins. When the Yarn Project is imported, the addon finds your Functions and tells the compiler what each one returns, so the compiler can work out how to show the value.

If the addon can’t find a Function’s return type, a Function used directly inside a line won’t compile, and you’ll see “Can’t determine the type of the expression”. That happens when:

  • the addon can’t find the Function’s script, because it isn’t in the Yarn Project’s folder or in a scene that uses the Yarn Project, or it’s outside the folder set in the Ysls Scan Path import option (see The .ysls.json file),
  • the Function’s method doesn’t say what it returns, like func _coin_count(): instead of func _coin_count() -> int:, or it’s a lambda,
  • the Yarn Project’s Generate Ysls import option is off.

Fix whichever applies, or store the value in a variable first and put the variable in the line:

<<set $coin_total to coin_count()>>
Rosa: You've got {$coin_total} coins.

Writing a Function

Functions the Dialogue Runner finds by itself

Give a static method a name that starts with _yarn_function_, and the rest of the name is the Function’s name in Yarn:

static func _yarn_function_shout(text: String) -> String:
	return text.to_upper()
A static Function that returns its text in capitals.

The Dialogue Runner finds these methods in the same places it finds Commands: in scripts with a class_name, in scripts attached to nodes in the running scene, and in autoloads. See Where the Dialogue Runner looks.

The method has to be static. If a _yarn_function_ method isn’t, the Dialogue Runner logs an error and skips it. A method in an autoload can be either.

Functions that use your game’s state

A static method can’t read the variables on a node, like an inventory. For a Function that needs to, add it to the Dialogue Runner with add_function():

@export var dialogue_runner: YarnDialogueRunner

var _inventory: Array[String] = ["lantern"]
var _coins := 15


func _ready():
	dialogue_runner.add_function("has_item", _has_item)
	dialogue_runner.add_function("coin_count", _coin_count)


func _has_item(item_name: String) -> bool:
	return item_name in _inventory


func _coin_count() -> int:
	return _coins
Line 1 The Dialogue Runner, set in the Inspector.
Lines 3–4 The game state the Functions read. A static method couldn’t see these.
Lines 7–9 Add both Functions when the scene starts. The first argument is each one’s name in Yarn.
Lines 12–13 has_item("brass key") in Yarn calls this. It returns a bool, so it can be used in an <<if>>.
Lines 16–17 coin_count() returns a number.
Adding two Functions that read the player’s inventory and coins.

Pass a named method, like _has_item, so the Dialogue Runner can see its parameters. If you pass a lambda, give the number of arguments it takes as a third argument: dialogue_runner.add_function("double", func(n): return n * 2, 1).

To remove a Function, call dialogue_runner.remove_function("has_item"). To add a Function to every Dialogue Runner in your game, call YarnSpinner.register_function(), with the same arguments, before the Dialogue Runners start.

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

Values going in and out

A Function has to return one of the kinds of value Yarn understands:

Return typeIn Yarn
booltrue or false.
int or floatA number.
StringText.
What a Function can return.

A Function must return its value immediately. It can’t await anything. If it returns something else, returns nothing, or awaits, the Dialogue Runner logs an error and stops the dialogue.

Arguments are converted to the types of the method’s parameters. A Yarn number becomes an int or float, and true and false become a bool. A parameter without a type gets the value as it is. Parameters with default values can be left out.

If a Function gets the wrong number of arguments, or one can’t be converted, the Dialogue Runner logs an error and stops the dialogue.

Built-in Functions

These Functions are always available:

FunctionWhat it returns
visited("NodeName")true if the node has been run before.
visited_count("NodeName")How many times the node has been run.
random()A random number between 0 and 1.
random_range(1, 6)A random whole number from the first number to the second, including both.
dice(6)A random whole number from 1 to the number you give it, like rolling a die.
round(n), floor(n), ceil(n)The number rounded to the nearest whole number, down, or up.
min(a, b), max(a, b)The smaller or larger of two numbers.
Some of the built-in Functions.

For maths there are random_range_float, round_places, inc, dec, decimal, int, abs, sign, clamp, lerp, inverse_lerp, smoothstep, pow, sqrt, wrap and mod. For converting between types there are string, number and bool.

Autocomplete in VS Code

The Yarn Spinner extension for VS Code suggests your Functions as you type, along with what they return. It reads them from the project’s .ysls.json file, which the addon writes for you, including Functions you add with add_function(). See The .ysls.json file.

The scene in the video

GameNode2D
BackgroundColorRect
FloorColorRect
DoorColorRect
LampColorRect
GlowColorRect
RosaNode2D
BodyColorRect
NameLabel
YarnDialogueRunner
UICanvasLayer
CallLogPanelPanelContainer
CallLogRichTextLabel
InventoryPanelPanelContainer
InventoryLabel
YarnLinePresenter
The demo scene. Game’s script has the Functions.
title: FunctionsDemo
---
<<declare $lamp_days = 0>>
<<set $lamp_days to days_since_lamp_lit()>>
Rosa: The lamp's been out for {$lamp_days} days.
<<if has_item("brass key")>>
    Rosa: Good, you've got the key.
<<else>>
    Rosa: You'll need a key to get up the stairs.
<<endif>>
<<declare $coin_total = 0>>
<<set $coin_total to coin_count()>>
Rosa: You've got {$coin_total} coins.
<<if coin_count() >= 10>>
    Rosa: That's enough to buy oil for the lamp.
<<endif>>
<<declare $sign_text = "">>
<<set $sign_text to shout("keep out")>>
Rosa: The sign on the door says "{$sign_text}".
===
The Yarn Script. Each Function’s value is stored in a variable before it’s shown in a line.
extends Node2D

@export var dialogue_runner: YarnDialogueRunner
@export var call_log: RichTextLabel
@export var inventory_label: Label

var _inventory: Array[String] = ["lantern"]
var _coins := 15

static var _log: RichTextLabel


func _ready():
	_log = call_log
	dialogue_runner.add_function("days_since_lamp_lit", _days_since_lamp_lit)
	dialogue_runner.add_function("has_item", _has_item)
	dialogue_runner.add_function("coin_count", _coin_count)
	_update_inventory()


static func _log_function(call: String, result: Variant) -> void:
	var shown := '"%s"' % result if result is String else str(result)
	_log.append_text("[color=#7fc4e8]Function[/color] %s → %s\n" % [call, shown])


func _update_inventory() -> void:
	var text := "Inventory\n"
	for item in _inventory:
		text += "• " + item + "\n"
	text += "• %d coins" % _coins
	inventory_label.text = text


# Functions added in _ready

func _days_since_lamp_lit() -> int:
	var days := 12
	_log_function("days_since_lamp_lit()", days)
	return days


func _has_item(item_name: String) -> bool:
	var result := item_name in _inventory
	_log_function('has_item("%s")' % item_name, result)
	return result


func _coin_count() -> int:
	_log_function("coin_count()", _coins)
	return _coins


# A Function the Dialogue Runner finds by itself

static func _yarn_function_shout(text: String) -> String:
	var result := text.to_upper()
	_log_function('shout("%s")' % text, result)
	return result
Lines 3–5 Nodes in the scene, set in the Inspector.
Lines 7–8 The game state the Functions read.
Lines 10 & 14 The log panel, kept in a static variable so the static Function can write to it too. This is only for the demo.
Lines 15–17 Add three Functions. shout isn’t here: it’s static and named _yarn_function_shout, so the Dialogue Runner finds it by itself.
Lines 21–23 Write each call and its result to the panel in the top left. This is only for the demo.
Lines 26–31 Show the inventory and coins in the top right.
Lines 36–39 days_since_lamp_lit() returns a number.
Lines 42–45 has_item() returns a bool.
Lines 48–50 coin_count() returns the number of coins.
Lines 55–58 shout() is static, so it can’t read this node’s variables. It returns its text in capitals.
The script on the Game node. It adds three Functions in _ready, and has one static Function that the Dialogue Runner finds by itself. _log_function writes each call to the panel in the top left.
Next step Putting It Together A small scene that uses Commands and Functions together.