This documentation is a reference for SugarCube, a free (gratis and libre) story format for Twine/Twee.
If you believe that you've found a bug in SugarCube or simply wish to make a suggestion, you may do so by creating a new issue at its code repository.
WARNING: This new single-page version of the documentation is still a bit rough.
In addition to using one of the print macros (<<print>>, <<=>>, <<->>) to print the values of TwineScript variables, SugarCube's naked variable markup allows printing them simply by including them within your normal passage text (i.e. variables in passage text are interpolated into their values).
The following forms are supported by the naked variable markup:
| Type | Syntax | Example |
|---|---|---|
| Simple variable | $variable |
$name |
| Property access, dot notation |
$variable.property |
$thing.name |
| Index/property access, square bracket notation |
|
|
If you need to print anything more complex (e.g. using a calculation, $variable[_i + 1], or a method call, $variable.someMethod()), then you will still need to use one of the print macros.
For example:
/* Explicitly printing the value of $name via the <<print>> macro */
Well hello there, <<print $name>>.
/* Implicitly printing the value of $name via the naked variable markup */
Well hello there, $name.
/* Assuming $name is set to "Mr. Freeman", both should yield the following */
Well hello there, Mr. Freeman.
Because variables within your passage text are transformed into their values automatically, if you actually want to output a variable as-is (i.e. without interpolation; e.g. for a tutorial, debug output, or whatever), then you'll need to escape it in some fashion. For example:
/* Using the nowiki markup: """…""" (triple double-quotes) */
The variable """$name""" is set to: $name
/* Using the nowiki markup: <nowiki>…</nowiki> */
The variable <nowiki>$name</nowiki> is set to: $name
/* Using the double dollar-sign markup (which escapes the $-sigil): $$ */
The variable $$name is set to: $name
/* Assuming $name is set to "Mr. Freeman", all of the above should yield the following */
The variable $name is set to: Mr. Freeman
Additionally, you could use the inline code markup to escape the variable, though it will also wrap the escaped variable within a <code> element, so it's probably best used for examples and tutorials. For example:
/* Using the inline code markup: {{{…}}} (triple curly braces) */
The variable {{{$name}}} is set to: $name
/* Assuming $name is set to "Mr. Freeman", it should yield the following */
The variable <code>$name</code> is set to: Mr. Freeman
SugarCube's link markup consists of a required Link component and optional Text and Setter components. The Link and Text components may be either plain text or any valid TwineScript expression, which will be evaluated early (i.e. when the link is initially processed). The Setter component (which only works with passage links, not external links) must be a valid TwineScript expression, of the <<set>> macro variety, which will be evaluated late (i.e. when the link is clicked on).
The Link component value may be the title of a passage or any valid URL to a resource (local or remote).
In addition to the standard pipe separator (|) used to separate the Link and Text components (as seen below), SugarCube also supports the arrow separators (-> & <-). Particular to the arrow separators, the arrows' direction determines the order of the components, with the arrow always pointing at the Link component (i.e. the right arrow works like the pipe separator, Text->Link, while the left arrow is reversed, Link<-Text).
| Syntax | Example |
|---|---|
|
|
|
|
|
|
|
|
SugarCube's image markup consists of a required Image component and optional Title, Link, and Setter components. The Image, Title, and Link components may be either plain text or any valid TwineScript expression, which will be evaluated early (i.e. when the link is initially processed). The Setter component (which only works with passage links, not external links) must be a valid TwineScript expression, of the <<set>> macro variety, which will be evaluated late (i.e. when the link is clicked on).
The Image component value may be any valid URL to an image resource (local or remote) or the title of an embedded image passage (Twine 1 & Tweego only). The Link component value may be the title of a passage or any valid URL to a resource (local or remote).
In addition to the standard pipe separator (|) used to separate the Image and Title components (as seen below), SugarCube also supports the arrow separators (-> & <-). Particular to the arrow separators, the arrows' direction determines the order of the components, with the arrow always pointing at the Image component (i.e. the right arrow works like the pipe separator, Title->Image, while the left arrow is reversed, Image<-Title).
| Syntax | Example |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
A restricted subset of the image markup, allowing only the Image component, may be used within stylesheets (primarily to allow the easy use of image passages). For example:
/* Using the external image "forest.png" as the <body> background. */
body {
background-image: [img[forest.png]];
}
/* Using the image passage "lagoon" as the <body> background. */
body {
background-image: [img[lagoon]];
}
NOTE: None of these features work with the verbatim HTML parser (<html>…</html>).
SugarCube provides a few special HTML attributes, which you may add to HTML tags to enable special behaviors. There are attributes for passage links, media passages, and setters.
| Type | Attribute | Example |
|---|---|---|
| Passage, Link | |
|
| Passage, Audio | |
|
| Passage, Image | |
|
| Passage, Source | |
|
| Passage, Video | |
|
| Setter | |
|
v2.0.0: Basic syntax.v2.24.0: Added data-passage attribute support to <audio>, <source>, and <video> tags.
HTML attributes may be prefixed with directives, special text, which trigger special processing of such attributes.
sc-eval:, @The evaluation directive causes the attribute's value to be evaluated as TwineScript. Post-evaluation, the directive will be removed from the attribute's name and the result of the evaluation will be used as the actual value of the attribute.
| Syntax | Example | Rendered As |
|---|---|---|
sc-eval:attribute-name |
|
|
@attribute-name |
|
|
v2.21.0
A backslash (\) which begins or ends a line is the line continuation markup. The line continuation markup causes the backslash and the associated line break to be removed upon processing, thus joining the nearby lines together. This is mostly useful for controlling whitespace when you want to wrap lines for readability, but not generate extra whitespace upon display, and the <<silently>> macro isn't an option because you need to generate output.
For example, both of the following:
The rain in Spain falls \
mainly on the plain.
The rain in Spain falls
\ mainly on the plain.
Yield the single line in the final output:
The rain in Spain falls mainly on the plain.
NOTE: The <<nobr>> macro, nobr special tag, and Config.passages.nobr setting all perform a similar, though slightly different, function.
An exclamation point which begins a line defines the heading markup. It consists of one to six exclamation points, each additional one beyond the first signifying a lesser heading.
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Level 1 | |
|
Level 1 Heading |
| Level 2 | |
|
Level 2 Heading |
| Level 3 | |
|
Level 3 Heading |
| Level 4 | |
|
Level 4 Heading |
| Level 5 | |
|
Level 5 Heading |
| Level 6 | |
|
Level 6 Heading |
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Emphasis | |
|
Emphasis |
| Strong | |
|
Strong |
| Underline | |
|
Underline |
| Strikethrough | |
|
|
| Superscript | |
|
Superscript |
| Subscript | |
|
Subscript |
An asterisk or number sign which begins a line defines a member of the unordered or ordered list markup, respectively.
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Unordered | |
|
|
| Ordered | |
|
|
A right angle bracket (i.e. greater-than sign) which begins a line defines the blockquote markup. It consists of one or more right angle brackets, each additional one beyond the first signifying a level of nested blockquote.
| Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|
|
|
Line 1 |
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Inline | |
|
|
| Block | |
|
|
A set of four hyphen/minus characters which begins a line defines the horizontal rule markup.
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Horizontal rule | |
|
| Type | Syntax & Example | Rendered As | Displays As (roughly) |
|---|---|---|---|
| Triple double quotes | |
|
No //format// |
| <nowiki> tag | |
|
No //format// |
| Type | Syntax | Example | Rendered As |
|---|---|---|---|
| Inline | |
|
|
|
|
||
| Block | |
|
|
|
|
;) separated list consisting of a single hash-prefixed ID (which should be unique; e.g. #foo) and/or any number of dot-prefixed class names (e.g. .bar) and/or style properties (e.g. color:red).
NOTE: Comments used within passage markup are not rendered into the page output.
| Type | Syntax & Example | Supported Within… |
|---|---|---|
| C-style, Block | |
Passage markup, JavaScript, Stylesheets |
| TiddlyWiki, Block | |
Passage markup |
| HTML, Block | |
Passage markup |
TwineScript in SugarCube is, essentially, JavaScript with an extra spoonful of sugar on top to make it a bit nicer for the uninitiated.
See the Variables and About Expressions sections of the Twine 1/Twee reference documentation for additional information.
NOTE: Temporary variables were added in v2.3.0.
A variable is a bit of storage where you may stash a value for later use. In SugarCube, they come in two types: story variables and temporary variables. Story variables are a part of the story history and exist for the lifetime of a playthrough session. Temporary variables do not become part of the story history and only exist for the lifetime of the moment/turn that they're created in. You'll likely use story variables most often throughout your project—though, temporary variables are perfect candidates for things like loop variables, if you're using the <<for>> macro.
For example, you might use the story variable $name to store the main player character's name or the story variable $cash to store how much money the player has on hand.
Values may be of most primitive types and some object types, see Supported Types for more information.
The names of both story and temporary variables have a certain format that they must follow—which signifies that they are variables and not some other kind of data.
The very first, and mandatory, character is their sigil, which denotes whether they are a story or temporary variable. The sigil must be a dollar sign ($) for story variables or an underscore (_) for temporary variables.
The second, and also mandatory, character of the variable name may be one of the following: the letters A though Z (in upper or lower case), the dollar sign, and the underscore (i.e. A-Za-z$_)—yes, after their initial use as the sigil, the dollar sign and underscore become regular variable characters.
Subsequent, optional, characters have the same set as the second with the addition of numerals (i.e. 0-9, so the full set is A-Za-z0-9$_). No other characters are allowed.
A few examples of valid names:
/* Story variables */
$cash
$hasKeyCard5
$met_alice
$TIMES_POKED_MR_BEAR
/* Temporary variables */
_i
_something2
_some_loop_value
_COUNT
NOTE: This is not an exhaustive list. There are many ways to use and interact with variables.
To modify the values contained within variables, see the <<set>> macro and setter links.
To print the values contained within variables, see the naked variable markup and the <<print>>, <<=>>, and <<->> macros.
To control aspects of your project based on the values contained within variables, see the <<if>> and <<switch>> macros.
The following types of values are natively supported by SugarCube and may be safely used within story and temporary variables.
true & false)42, 3.14, & -17.01)"I like pie" & 'You like pie')nullundefinedAny supported object type may itself contain any supported primitive or object type.
Unsupported object types, either native or custom, can be made compatible by implementing .clone() and .toJSON() methods for them.
Expressions are simply units of code that yield values when evaluated. For example:
2 + 2 → Yields: 4
"a" + "bc" → Yields: "abc"
turns() → Yields: 1 (assuming that it is the first turn)
While every valid expression—even those you might not expect—yields a value, there are essentially two types of expressions: those with side effects and those without. A side effect simply means that the evaluation of the expression modifies some state. For example:
$a = 5 → Yields: 5; Side effect: assigns 5 to the story variable $a
$x + 10 → Yields: 25 (assuming $x is 15); No side effects
In general, you can group expressions into categories based on what kind of value they yield and/or what side effects they cause. For example: (not an exhaustive list)
42 or 3.14)."Lulamoon" or "5678").true or false).$a = 5).You will, in all likelihood, use expressions most often within macros—e.g. <<set>>, <<print>>, <<if>>, <<for>>.
Macros fall into two broad categories based on the kind of arguments they accept: those that want an expression (e.g. <<set>> and <<print>>) and those that want discrete arguments separated by whitespace (e.g. <<link>> and <<audio>>). The documentation for each macro will tell you what it expects.
Those that want an expression are fairly straightforward, as you simply supply an expression.
The discrete argument type of macros are also fairly straightforward, most of the time, as you simply supply the requisite arguments separated by whitespace. There are cases, however, where things get a bit more complicated, namely: instances where you need to pass the name of a variable as an argument and those where you want to pass the result of an expression as argument.
Passing the name of a variable as an argument is problematic because variable substitution occurs automatically in SugarCube macros. Meaning that when you pass a variable as an argument, its value is passed to the macro rather than its name.
Normally, this is exactly what you want to happen. Occasionally, however, a macro will need the name of a variable rather than its value—generally, data input macros (e.g. <<textbox>>) so that they may modify the value of the variable. To resolve these instances, you will need to quote the name of the variable (i.e. instead of passing $pie as normal, you'd pass "$pie"). These, rare, instances are noted in the macros' documentation and shown in their examples.
Passing the result of an expression as an argument is problematic for a couple of reasons: because the macro argument parser doesn't treat arguments as expressions by default and because it separates arguments with whitespace.
Normally, those aren't issues as you should not need to use the result of an expression as an argument terribly often. To resolve instances where you do, however, you'll want to use either a temporary variable or a backquote expression.
For example, the following will not work because the macro parser will think that you're passing five discrete arguments, rather than a single expression:
<<link "Wake " + $friend + ".">> … <</link>>
You could solve the problem by using a temporary variable to hold the result of the expression, then pass that to the macro. For example:
<<set _text to "Wake " + $friend + ".">>\
<<link _text>> … <</link>>
A better solution, however, would be to use a backquote1 expression (`…`), which is really just a special form of quoting available in macro arguments that causes the contents of the backquotes to be evaluated and then yields the result as a singular argument. For example:
<<link `"Wake " + $friend + "."`>> … <</link>>
~) on keyboards.
<<capture variableList>> … <</capture>>Captures story $variables and temporary _variables, creating localized versions of their values within the macro body.
NOTE: Use of this macro is only necessary when you need to localize a variable's value for use within an asynchronous container/non-void macro's body (i.e. a macro whose contents are executed at some later time, rather than when it's invoked; e.g. <<button>>, <<link>>, <<linkappend>>, <<linkprepend>>, <<linkreplace>>, <<repeat>>, <<timed>>). Generally, this means only when the variable's value will change between the time the asynchronous macro is invoked and when it's activated (e.g. a loop variable).
v2.14.0variableList: A list of story $variables and/or temporary _variables.→ Using <<capture>> to localize a temporary loop variable for use within a <<linkappend>>
<<set _what to [
"a crab rangoon",
"a gaggle of geese",
"an aardvark",
"the world's smallest violin"
]>>
<<for _i to 0; _i lt _what.length; _i++>>
<<capture _i>>
I spy with my little <<linkappend "eye" t8n>>, _what[_i]<</linkappend>>.
<</capture>>
<</for>>
→ Capturing several variables at once
<<capture $aStoryVar, $anotherStoryVar, _aTempVar>> … <</capture>>
<<set expression>>Sets story $variables and temporary _variables based on the given expression.
v2.0.0expression: A valid expression. See Variables and Expressions for more information.| Operator | Description | Example |
|---|---|---|
to |
Assigns the value on the right-hand side of the operator to the left-hand side. | <<set $apples to 6>> |
| Operator | Description | Example |
|---|---|---|
= |
Assigns the value on the right-hand side of the operator to the left-hand side. | <<set $apples = 6>> |
+= |
Adds the value on the right-hand side of the operator to the current value on the left-hand side and assigns the result to the left-hand side. | <<set $apples += 1>> |
-= |
Subtracts the value on the right-hand side of the operator from the current value on the left-hand side and assigns the result to the left-hand side. | <<set $apples -= 1>> |
*= |
Multiplies the current value on the left-hand side of the operator by the value on the right-hand side and assigns the result to the left-hand side. | <<set $apples *= 2>> |
/= |
Divides the current value on the left-hand side of the operator by the value on the right-hand side and assigns the result to the left-hand side. | <<set $apples /= 2>> |
%= |
Divides the current value on the left-hand side of the operator by the value on the right-hand side and assigns the remainder to the left-hand side. | <<set $apples %= 10>> |
→ Using the TwineScript "to" operator
<<set $cheese to "a nice, sharp cheddar">> → Assigns "a nice, sharp cheddar" to story variable $cheese
<<set $chestEmpty to true>> → Assigns boolean true to story variable $chestEmpty
<<set $gold to $gold + 5>> → Adds 5 to the value of story variable $gold
<<set _counter to _counter + 1>> → Adds 1 to the value of temporary variable _counter
→ Using standard JavaScript operators
<<set $cheese = "a nice, sharp cheddar">> → Assigns "a nice, sharp cheddar" to story variable $cheese
<<set $chestEmpty = true>> → Assigns boolean true to story variable $chestEmpty
<<set $gold += 5>> → Adds 5 to the value of story variable $gold
<<set _counter += 1>> → Adds 1 to the value of temporary variable _counter
<<unset variableList>>Unsets story $variables and temporary _variables.
v2.0.0variableList: A list of story $variables and/or temporary _variables.<<unset $cheese, $chestEmpty, $gold>>
<<unset _someTempVar>>
<<remember expression>>Functionally identical to <<set>>, save that it also causes the values of story $variables to persist over page reloads, game restarts, and even browser restarts. Does not cause temporary _variables to persist.
NOTE: Generally, you do not need, or want, to use <<remember>>, as it is only useful in very specific circumstances and problematic in most others. Unless you know that you need to use it, you very likely do not.
<<forget variableList>>Functionally identical to <<unset>>, save that it also removes the story $variables from the <<remember>> store. Does not affect temporary _variables.
<<run expression>>Functionally identical to <<set>>. Intended to be mnemonically better for uses where the expression is arbitrary code, rather than variables to set—i.e. <<run>> to run code, <<set>> to set variables.
<<script>> … <</script>>Silently executes its contents as pure JavaScript code—i.e. it performs no story or temporary variable substitution or TwineScript operator processing. For instances where you need to run some pure JavaScript and don't want to waste time performing extra processing on code that has no story or temporary variables or TwineScript operators in it and/or worry about the parser possibly clobbering the code.
NOTE: The predefined variable output, which is a reference to a local content buffer, is available for use within the macro's code contents. Once the code has been fully executed, the contents of the buffer, if any, will be output.
v2.0.0→ Basic
<<script>>
/* pure JavaScript code */
<</script>>
→ Modifying the content buffer
<<script>>
/* Parse some markup and append the result to the output buffer. */
$(output).wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
<</script>>
<<= expression>>Functionally identical to <<print>>.
<<- expression>>Functionally identical to <<print>>, save that it also encodes HTML special characters in the output.
<<include passageName [elementName]>><<include linkMarkup [elementName]>>Outputs the contents of the passage with the given name, optionally wrapping it within an HTML element. May be called either with the passage name or with a link markup.
v2.15.0passageName: The name of the passage to include.elementName: (optional) The HTML element to wrap the included passage in. If used, the element will include the passage's name normalized into a class name. See CSS passage conversions for more information.linkMarkup: The link markup to use (regular syntax only, no setters).elementName: Identical to the passage name form.<<include "Go West">> → Include the passage "Go West"
<<include [[Go West]]>> → Include the passage "Go West"
<<include "Go West" "div">> → Include the passage "Go West", wrapping it within a <div>
<<include [[Go West]] "div">> → Include the passage "Go West", wrapping it within a <div>
<<nobr>> … <</nobr>>Executes its contents and outputs the result, after removing leading/trailing newlines and replacing all remaining sequences of newlines with single spaces.
NOTE: The nobr special tag and Config.passages.nobr setting applies the same processing to an entire passage or all passages, respectively. The line continuation markup performs a similar function, though in a slightly different way.
v2.0.0→ Given: $feeling eq "blue", outputs: I'd like a blueberry pie.
I'd like a <<nobr>>
<<if $feeling eq "blue">>
blueberry
<<else>>
cherry
<</if>>
<</nobr>> pie.
<<print expression>>Outputs the result of the given expression.
NOTE: If you only need to print the value of a TwineScript variable, then you may simply include it in your normal passage text and it will be printed automatically via the naked variable markup.
v2.0.0expression: A valid expression. See Expressions for more information.→ Assuming $gold is 5
You found <<print $gold>> gold. → Outputs: You found 5 gold.
→ Assuming $weight is 74.6466266
You weigh <<print $weight.toFixed(2)>> kg. → Outputs: You weigh 74.65 kg.
<<silently>> … <</silently>>Causes any output generated within its body to be discarded, except for errors (which will be displayed). Generally, only really useful for formatting blocks of macros for ease of use/readability, while ensuring that no output is generated, from spacing or whatnot.
v2.0.0→ Basic
<<silently>>
You'll never see any of this!
<</silently>>
→ Hiding the guts of a countdown timer
<<set $seconds to 10>>\
Countdown: <span id="countdown">$seconds seconds remaining</span>!\
<<silently>>
<<repeat 1s>>
<<set $seconds to $seconds - 1>>
<<if $seconds gt 0>>
<<replace "#countdown">>$seconds seconds remaining<</replace>>
<<else>>
<<replace "#countdown">>Too Late<</replace>>
/* do something useful here */
<<stop>>
<</if>>
<</repeat>>
<</silently>>
<<display passageName [elementName]>><<display linkMarkup [elementName]>>Deprecated:
This macro has been deprecated and should no longer be used. See the <<include>> macro for its replacement.
v2.0.0: Basic syntax.v2.15.0: Deprecated in favor of <<include>>.
<<if conditional>> … [<<elseif conditional>> …] [<<else>> …] <</if>>Executes its contents if the given conditional expression evaluates to true. If the condition evaluates to false and an <<elseif>> or <<else>> exists, then other contents can be executed.
NOTE: SugarCube does not trim whitespace from the contents of <<if>> macros, so that authors don't have to resort to various kludges to get whitespace where they want it. This means, however, that extra care must be taken when writing them to ensure that unwanted whitespace is not created within the final output.
v2.0.0conditional: A valid conditional expression, evaluating to either true or false. See Expressions for more information.| Operator | Description | Example |
|---|---|---|
is |
Evaluates to true if both sides are strictly equal. |
<<if $bullets is 6>> |
isnot |
Evaluates to true if both sides are strictly not equal. |
<<if $pie isnot "cherry">> |
eq |
Evaluates to true if both sides are equivalent. |
<<if $bullets eq 6>> |
neq |
Evaluates to true if both sides are not equivalent. |
<<if $pie neq "cherry">> |
gt |
Evaluates to true if the left side is greater than the right side. |
<<if $cash gt 5>> |
gte |
Evaluates to true if the left side is greater than or equal to the right side. |
<<if $foundStars gte $neededStars>> |
lt |
Evaluates to true if the left side is less than the right side. |
<<if $shoeCount lt ($peopleCount * 2)>> |
lte |
Evaluates to true if the left side is less than or equal to the right side. |
<<if $level lte 30>> |
not |
Flips a true evaluation to false, and vice versa. |
<<if not $hungry>> |
and |
Evaluates to true if all subexpressions evaluate to true. |
<<if $age gte 20 and $age lte 30>> |
or |
Evaluates to true if any subexpressions evaluate to true. |
<<if $friend is "Sue" or $friend is "Dan">> |
def |
Evaluates to true if the right side is defined. |
<<if def $mushrooms>> |
ndef |
Evaluates to true if the right side is not defined. |
<<if ndef $bottlecaps>> |
NOTE: The def and ndef operators have very low precedence, so it is strongly recommended that if you mix them with other operators, that you wrap them in parentheses—e.g. (def $style) and ($style is "girly").
| Operator | Description | Example |
|---|---|---|
=== |
Evaluates to true if both sides are strictly equal. |
<<if $bullets === 6>> |
!== |
Evaluates to true if both sides are strictly not equal. |
<<if $pie !== "cherry">> |
== |
Evaluates to true if both sides are equivalent. |
<<if $bullets == 6>> |
!= |
Evaluates to true if both sides are not equivalent. |
<<if $pie != "cherry">> |
> |
Evaluates to true if the left side is greater than the right side. |
<<if $cash > 5>> |
>= |
Evaluates to true if the left side is greater than or equal to the right side. |
<<if $foundStars >= $neededStars>> |
< |
Evaluates to true if the left side is less than the right side. |
<<if $shoeCount < ($peopleCount * 2)>> |
<= |
Evaluates to true if the left side is less than or equal to the right side. |
<<if $level <= 30>> |
! |
Flips a true evaluation to false, and vice versa. |
<<if !$hungry>> |
&& |
Evaluates to true if all subexpressions evaluate to true. |
<<if $age >= 20 && $age <= 30>> |
|| |
Evaluates to true if any subexpressions evaluate to true. |
<<if $friend === "Sue" || $friend === "Dan">> |
<<if $daysUntilLoanDue is 0>><<include "Panic">><</if>>
<<if $cash lt 5>>
I'm sorry, ma'am, but you don't have enough for the pie.
<<else>>
<<set $cash -= 5, $hasMeatPie = true>>
One meat pie, fresh out of the oven, coming up!
<</if>>
<<if $affection gte 75>>
I love you!
<<elseif $affection gte 50>>
I like you.
<<elseif $affection gte 25>>
I'm okay with you.
<<else>>
Get out of my face.
<</if>>
<<if $hullBreached>>
<<if $wearingHardSuit>>
<<include "That was close">>
<<elseif $wearingSoftSuit>>
<<include "Hole in suit">>
<<else>>
<<include "You die">>
<</if>>
<</if>>
<<for [conditional]>> … <</for>><<for [init] ; [conditional] ; [post]>> … <</for>><<for [keyVariable ,] valueVariable range collection>> … <</for>>Repeatedly executes its contents. There are three forms: a conditional-only form, a 3-part conditional form, and a range form.
v2.0.0: Basic syntax.v2.20.0: Added range form._i.Executes its contents while the given conditional expression evaluates to true. If no conditional expression is given, it is equivalent to specifying true.
NOTE: The maximum number of loop iterations in the conditional forms is not unlimited by default, however, it is configurable. See Config.macros.maxLoopIterations for more information.
init: (optional) A valid expression, evaluated once at loop initialization. Typically used to initialize counter variable(s). See <<set>> for more information.conditional: (optional) A valid conditional expression, evaluated prior to each loop iteration. As long as the expression evaluates to true, the loop is executed. See <<if>> for more information.post: (optional) A valid expression, evaluated after each loop iteration. Typically used to update counter variable(s). See <<set>> for more information.→ Example setup
<<set $dwarves to ["Doc", "Dopey", "Bashful", "Grumpy", "Sneezy", "Sleepy", "Happy"]>>
→ Loop
<<for _i to 0; _i lt $dwarves.length; _i++>>
<<print _i + 1>>. $dwarves[_i]
<</for>>
→ Result
1. Doc
2. Dopey
3. Bashful
4. Grumpy
5. Sneezy
6. Sleepy
7. Happy
Iterates through all enumerable entries of the given collection. For each iteration, it assigns the key/value pair of the associated entry in the collection to the iteration variables and then executes its contents. Valid collection types are: arrays, generic objects, maps, sets, and strings.
keyVariable: (optional) A story or temporary variable which will be set to the iteration key.valueVariable: A story or temporary variable which will be set to the iteration value.range: Keyword, used to signify that the loop is using the range form syntax.collection: An expression which yields a valid collection type, evaluated once at loop initialization.| Collection type | Iteration: key, value |
|---|---|
| Arrays & Sets | Member: index, value |
| Generic objects | Property: name, value |
| Maps | Entry: key, value |
| Strings | Code point: start index, value |
NOTE: Strings are iterated by Unicode code point, however, due to historic reasons they are comprised of, and indexed by, individual UTF-16 code units. This means that some code points may span multiple code units—e.g. the character 💩 is one code point, but two code units.
→ Example setup
<<set $dwarves to ["Doc", "Dopey", "Bashful", "Grumpy", "Sneezy", "Sleepy", "Happy"]>>
→ Loop
<<for _i, _name range $dwarves>>
<<print _i + 1>>. _name
<</for>>
→ Result
1. Doc
2. Dopey
3. Bashful
4. Grumpy
5. Sneezy
6. Sleepy
7. Happy
<<break>>Used within <<for>> macros. Terminates the execution of the current <<for>>.
v2.0.0
<<continue>>Used within <<for>> macros. Terminates the execution of the current iteration of the current <<for>> and begins execution of the next iteration.
NOTE: May eat line-breaks in certain situations.
v2.0.0
<<switch expression>> [<<case valueList>> …] [<<default>> …] <</switch>>Evaluates the given expression and compares it to the value(s) within its <<case>> children. The value(s) within each case are compared to the result of the expression given to the parent <<switch>>. Upon a successful match, the matching case will have its contents executed. If no cases match and an optional default case exists, which must be the final case, then its contents will be executed. At most one case will execute.
NOTE: SugarCube does not trim whitespace from the contents of <<case>>/<<default>> macros, so that authors don't have to resort to various kludges to get whitespace where they want it. However, this means that extra care must be taken when writing them to ensure that unwanted whitespace is not created within the final output.
v2.7.2<<switch>>expression: A valid expression. See Expressions for more information.<<case>>valueList: A space separated list of values to compare against the result of the switch expression.→ Without a default case
<<switch $hairColor>>
<<case "red" "auburn">>
You ginger.
<<case "black" "brown">>
Dark haired, eh?
<<case "blonde">>
You may have more fun.
<</switch>>
→ With a default case (assume the passage is about a waterfall)
<<switch visited()>>
<<case 1>>
You gaze in wonder at the magnificent waterfall for the first time, awestruck by its natural beauty.
<<case 2 3>>
You once again gaze upon the magnificent waterfall.
<<case 4 5>>
Yet again, you find yourself looking upon the waterfall.
<<default>>
Oh, look. It's that waterfall again. Meh.
<</switch>>
WARNING: Interactive macros require player input. As a consequence, reloading the page or revisiting a passage will not restore the state of interactive macros. It is recommended that you only use interactive macros in instances where this will not be an issue or where you can workaround it.
<<button linkText [passageName]>> … <</button>><<button linkMarkup>> … <</button>><<button imageMarkup>> … <</button>>Functionally identical to <<link>>, save that it uses a button element (<button>) rather than an anchor element (<a>).
<<checkbox variableName uncheckedValue checkedValue [checked]>>Creates a checkbox, used to modify the value of the variable with the given name.
SEE: Interactive macro warning.
v2.0.0variableName: The name of the variable to modify, which must be quoted (e.g. "$foo"). Object and array property references are also supported (e.g. "$foo.bar", "$foo['bar']", & "$foo[0]").uncheckedValue: The value set by the checkbox when unchecked.checkedValue: The value set by the checkbox when checked.checked: (optional) Keyword, used to signify that the checkbox should be in the checked state.What pies do you enjoy?
* <<checkbox "$pieBlueberry" false true checked>> Blueberry?
* <<checkbox "$pieCherry" false true>> Cherry?
* <<checkbox "$pieCoconutCream" false true checked>> Coconut cream?
NOTE: For accessibility reasons, it's recommended that you wrap each <<checkbox>> and its accompanying text within a <label> element. Doing so allows interactions with the text to also trigger its <<checkbox>>. For example:
What pies do you enjoy?
* <label><<checkbox "$pieBlueberry" false true checked>> Blueberry?</label>
* <label><<checkbox "$pieCherry" false true>> Cherry?</label>
* <label><<checkbox "$pieCoconutCream" false true checked>> Coconut cream?</label>
<<link linkText [passageName]>> … <</link>><<link linkMarkup>> … <</link>><<link imageMarkup>> … <</link>>Creates a link which silently executes its contents when clicked, optionally forwarding the player to another passage. May be called either with the link text and passage name as separate arguments, with a link markup, or with an image markup.
SEE: Interactive macro warning.
NOTE: If you simply need a passage link which modifies variables, both the link markup and image markup offer setter variants.
v2.8.0linkText: The text of the link. May contain markup.passageName: (optional) The name of the passage to go to.linkMarkup: The link markup to use (regular syntax only, no setters).imageMarkup: The image markup to use (regular syntax only, no setters).→ Without forwarding: a very basic statistic setting example
Strength: <<set $pcStr to 10>><span id="stats-str"><<print $pcStr>></span> \
( <<link "[+]">><<set $pcStr++>><<replace "#stats-str">><<print $pcStr>><</replace>><</link>> \
| <<link "[-]">><<set $pcStr-->><<replace "#stats-str">><<print $pcStr>><</replace>><</link>> )
→ With forwarding: execute a script, then go to the specified passage
<<link "Onward, Reginald!" "On with the story">><<script>>/* (code) */<</script>><</link>>
<<link [[Onward, Reginald!|On with the story]]>><<script>>/* (code) */<</script>><</link>>
<<link [img[onward.jpg][On with the story]]>><<script>>/* (code) */<</script>><</link>>
<<linkappend linkText [transition|t8n]>> … <</linkappend>>Creates a single-use link which deactivates itself and appends its contents to its link text when clicked. Essentially, a combination of <<link>> and <<append>>.
SEE: Interactive macro warning.
v2.0.0linkText: The text of the link. May contain markup.transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Without a transition
We—We should <<linkappend "take">> away their METAL BAWKSES<</linkappend>>!
→ With a transition
I spy with my little <<linkappend "eye" t8n>>, a crab rangoon<</linkappend>>.
<<linkprepend linkText [transition|t8n]>> … <</linkprepend>>Creates a single-use link which deactivates itself and prepends its contents to its link text when clicked. Essentially, a combination of <<link>> and <<prepend>>.
SEE: Interactive macro warning.
v2.0.0linkText: The text of the link. May contain markup.transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Without a transition
You see a <<linkprepend "robot">>GIANT <</linkprepend>>.
→ With a transition
I <<linkprepend "like" t8n>>do not <</linkprepend>> lemons.
<<linkreplace linkText [transition|t8n]>> … <</linkreplace>>Creates a single-use link which deactivates itself and replaces its link text with its contents when clicked. Essentially, a combination of <<link>> and <<replace>>.
SEE: Interactive macro warning.
v2.0.0linkText: The text of the link. May contain markup.transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Without a transition
I'll have a <<linkreplace "cupcake">>slice of key lime pie<</linkreplace>>, please.
→ With a transition
<<linkreplace "You'll //never// take me alive!" t8n>>On second thought, don't hurt me.<</linkreplace>>
<<listbox variableName [autoselect]>>[<<option label value [selected]>> …] [<<optionsfrom collection>> …]<</listbox>>Creates a listbox, used to modify the value of the variable with the given name. The list options are populated via <<option>>.
SEE: Interactive macro warning.
v2.26.0: Basic syntax.v2.27.0: Added autoselect keyword.v2.28.0: <<optionsFrom>> child tag.v2.28.1: Fixed name of <<optionsfrom>> child tag, which was erroneously added as <<optionsFrom>> in v2.28.0.<<listbox>>variableName: The name of the variable to modify, which must be quoted (e.g. "$foo"). Object and array property references are also supported (e.g. "$foo.bar", "$foo['bar']", & "$foo[0]").autoselect: (optional) Keyword, used to signify that an option should be automatically selected as the listbox default based on the current value of the target variable. NOTE: Automatic option selection will fail on non-primitive values—i.e. on arrays and objects.<<option>>label: The label shown by the listbox for the option.value: The value set by the listbox when the option is selected.selected: (optional) Keyword, used to signify that the option should be the listbox default. Only one option may be so selected. If no options are selected as the default, the listbox will default to the first option, unless the listbox autoselect keyword is specified.<<optionsfrom>>collection: An expression which yields a valid collection type.
| Collection type | Option: label, value |
|---|---|
| Arrays & Sets | Member: value, value |
| Generic objects | Property: name, value |
| Maps | Entry: key, value |
<<option>>What's your favorite pie?
<<listbox "$pie" autoselect>>
<<option "Blueberry" "blueberry">>
<<option "Cherry" "cherry">>
<<option "Coconut cream" "coconut cream">>
<</listbox>>
<<optionsfrom>> with an array→ Given: _pieOptions = ["blueberry", "cherry", "coconut cream"]
What's your favorite pie?
<<listbox "$pie" autoselect>>
<<optionsfrom _pieOptions>>
<</listbox>>
<<optionsfrom>> with an generic object→ Given: _pieOptions = { "Blueberry" : "blueberry", "Cherry" : "cherry", "Coconut cream" : "coconut cream" }
What's your favorite pie?
<<listbox "$pie" autoselect>>
<<optionsfrom _pieOptions>>
<</listbox>>
<<radiobutton variableName checkedValue [checked]>>Creates a radio button, used to modify the value of the variable with the given name. Multiple <<radiobutton>> macros may be set up to modify the same variable, which makes them part of a radio button group.
SEE: Interactive macro warning.
v2.0.0variableName: The name of the variable to modify, which must be quoted (e.g. "$foo"). Object and array property references are also supported (e.g. "$foo.bar", "$foo['bar']", & "$foo[0]").checkedValue: The value set by the radio button when checked.checked: (optional) Keyword, used to signify that the radio button should be in the checked state.What's your favorite pie?
* <<radiobutton "$pie" "blueberry" checked>> Blueberry?
* <<radiobutton "$pie" "cherry">> Cherry?
* <<radiobutton "$pie" "coconut cream">> Coconut cream?
NOTE: For accessibility reasons, it's recommended that you wrap each <<radiobutton>> and its accompanying text within a <label> element. Doing so allows interactions with the text to also trigger its <<radiobutton>>. For example:
What's your favorite pie?
* <label><<radiobutton "$pie" "blueberry" checked>> Blueberry?</label>
* <label><<radiobutton "$pie" "cherry">> Cherry?</label>
* <label><<radiobutton "$pie" "coconut cream">> Coconut cream?</label>
<<textarea variableName defaultValue [autofocus]>>Creates a multiline text input block, used to modify the value of the variable with the given name.
SEE: Interactive macro warning.
v2.0.0variableName: The name of the variable to modify, which must be quoted (e.g. "$foo"). Object and array property references are also supported (e.g. "$foo.bar", "$foo['bar']", & "$foo[0]").defaultValue: The default value of the text block.autofocus: (optional) Keyword, used to signify that the text block should automatically receive focus. Only use the keyword once per page; attempting to focus more than one element is undefined behavior.→ Creates a text block which modifies $pieEssay
Write a short essay about pies:
<<textarea "$pieEssay" "">>
→ Creates an automatically focused text block which modifies $pieEssay
Write a short essay about pies:
<<textarea "$pieEssay" "" autofocus>>
<<textbox variableName defaultValue [passage] [autofocus]>>Creates a text input box, used to modify the value of the variable with the given name, optionally forwarding the player to another passage.
SEE: Interactive macro warning.
v2.0.0variableName: The name of the variable to modify, which must be quoted (e.g. "$foo"). Object and array property references are also supported (e.g. "$foo.bar", "$foo['bar']", & "$foo[0]").defaultValue: The default value of the text box.passage: (optional) The name of the passage to go to if the return/enter key is pressed. May be called either with the passage name or with a link markup.autofocus: (optional) Keyword, used to signify that the text box should automatically receive focus. Only use the keyword once per page; attempting to focus more than one element is undefined behavior.→ Creates a text box which modifies $pie
What's your favorite pie? <<textbox "$pie" "Blueberry">>
→ Creates an automatically focused text box which modifies $pie
What's your favorite pie? <<textbox "$pie" "Blueberry" autofocus>>
→ Creates a text box which modifies $pie and forwards to the "Cakes" passage
What's your favorite pie? <<textbox "$pie" "Blueberry" "Cakes">>
→ Creates an automatically focused text box which modifies $pie and forwards to the "Cakes" passage
What's your favorite pie? <<textbox "$pie" "Blueberry" "Cakes" autofocus>>
<<click linkText [passageName]>> … <</click>><<click linkMarkup>><<click imageMarkup>>Deprecated:
This macro has been deprecated and should no longer be used. See the <<link>> macro for its replacement.
v2.0.0: Basic syntax.v2.8.0: Deprecated in favor of <<link>>.
<<actions passageList>><<actions linkMarkupList>><<actions imageMarkupList>>Creates a list of single-use passage links. Each link removes itself and all other <<actions>> links to the same passage after being activated. May be called either with a list of passages, with a list of link markup, or with a list of image markup. Probably most useful when paired with <<include>>. See the <<actions>> section of the Twine 1/Twee reference documentation for more information.
v2.0.0passageList: A space separated list of passage names.linkMarkupList: A space separated list of link markup to use (full syntax supported, including setters).imageMarkupList: A space separated list of image markup to use (full syntax supported, including setters).→ Passage list form
<<actions "Look at the pie" "Smell the pie" "Taste the pie">>
→ Link markup list form
<<actions [[Look at the pie]] [[Smell the pie]] [[Taste the pie]]>>
<<actions [[Look|Look at the pie]] [[Smell|Smell the pie]] [[Taste|Taste the pie]]>>
→ Image markup list form
<<actions [img[look.png][Look at the pie]] [img[smell.png][Smell the pie]] [img[taste.png][Taste the pie]]>>
<<back [linkText]>><<back linkMarkup>><<back imageMarkup>>Creates a link which undoes a number of passages/moments within the story history. May be called with, optional, link text or with a link or image markup.
v2.0.0linkText: (optional) The text of the link.linkMarkup: The link markup to use (regular syntax only, no setters).imageMarkup: The image markup to use (regular syntax only, no setters).<<back>> → Go back one passage/moment
→ Link text form
<<back "Home.">> → Go back one passage/moment, with link text "Home."
→ Link markup form
<<back [[HQ]]>> → Go back to the most recent "HQ" passage/moment
<<back [[Home.|HQ]]>> → Go back to the most recent "HQ" passage/moment, with link text "Home."
→ Image markup form
<<back [img[home.png]]>> → Go back one passage/moment, with link image "home.png"
<<back [img[home.png][HQ]]>> → Go back to the most recent "HQ" passage/moment, with link image "home.png"
<<return [linkText]>><<return linkMarkup>><<return imageMarkup>>Functionally identical to <<back>>, save that it pushes forward to the destination passage/moment, rather than popping back (i.e. it creates new history, rather than undoing existing history).
<<choice passageName [linkText]>><<choice linkMarkup>><<choice imageMarkup>>Creates a single-use passage link which deactivates itself and all other <<choice>> links within the originating passage when activated. May be called either with the passage name and link text as separate arguments, with a link markup, or with a image markup.
NOTE: Normally, when both link and text arguments are accepted, the order is text then link. However, due to a historical artifact, the arguments for the separate argument form of <<choice>> are in the reverse order (link then text).
v2.0.0passageName: The name of the passage to go to.linkText: (optional) The text of the link. If omitted, the passageName will be used instead.linkMarkup: The link markup to use (full syntax supported, including setters).imageMarkup: The image markup to use (full syntax supported, including setters).→ Separate argument form
<<choice "Take the red pill">>
<<choice $someAction>>
<<choice "Entered magic mirror" "Touch the strange mirror.">>
<<choice $go $show>>
→ Link markup form
<<choice [[Take the red pill]]>>
<<choice [[$someAction]]>>
<<choice [[Touch the strange mirror.|Entered magic mirror]]>>
<<choice [[$show|$go]]>>
→ Image markup form
<<choice [img[redpill.png][Take the red pill]]>>
<<choice [img[some-image.jpg][$someAction]]>>
<<choice [img[mirror.jpg][Entered magic mirror]]>>
<<choice [img[$show][$go]]>>
WARNING: All DOM macros require the elements to be manipulated to be on the page. As a consequence, you cannot use them directly within a passage to modify elements within said passage, since the elements they are targeting are still rendering and not yet on the page. You must, generally, use them with a interactive macro (e.g. <<link>>) or within the PassageDone special passage. Elements which are already part of the page, on the other hand, present no issues.
<<addclass selector classNames>>Adds classes to the selected element(s).
SEE: DOM macro warning.
v2.0.0selector: The CSS/jQuery-style selector used to target element(s).classNames: The names of the classes, separated by spaces.<<addclass "body" "day rain">> → Add the classes "day" and "rain" to the <body> element
<<addclass "#pie" "cherry">> → Add the class "cherry" to the element with the ID "pie"
<<addclass ".joe" "angry">> → Add the class "angry" to all elements containing the class "joe"
<<append selector [transition|t8n]>> … <</append>>Executes its contents and appends the output to the contents of the selected element(s).
SEE: DOM macro warning.
v2.0.0: Basic syntax.v2.25.0: Added transition and t8n keywords.selector: The CSS/jQuery-style selector used to target element(s).transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Example setup
I saw a <span id="dog">dog</span>.
→ Append to the contents of the target element
<<link "Doing">>
<<append "#dog">> chasing a cat<</append>>
<</link>>
→ Result, after clicking
I saw a <span id="dog">dog chasing a cat</span>.
→ Example setup
I saw a <span id="dog">dog</span>.
→ Append to the contents of the target element
<<link "Doing">>
<<append "#dog" t8n>> chasing a cat<</append>>
<</link>>
→ Result, after clicking
I saw a <span id="dog">dog<span class="macro-append-insert"> chasing a cat</span></span>.
<<copy selector>>Outputs a copy of the contents of the selected element(s).
SEE: DOM macro warning.
v2.0.0selector: The CSS/jQuery-style selector used to target element(s).→ Example setup
I'd like a <span id="snack-source">slice of Key lime pie</span>, please.
I'll have a <span id="snack-dest">breadstick</span>, thanks.
→ Replace the contents of the source target element with a copy of the destination target element
<<link "Have the same">>
<<replace "#snack-dest">><<copy "#snack-source">> too<</replace>>
<</link>>
→ Result, after the click
I'd like a <span id="snack-source">slice of Key lime pie</span>, please.
I'll have a <span id="snack-dest">slice of Key lime pie too</span>, thanks.
<<prepend selector [transition|t8n]>> … <</prepend>>Executes its contents and prepends the output to the contents of the selected element(s).
SEE: DOM macro warning.
v2.0.0: Basic syntax.v2.25.0: Added transition and t8n keywords.selector: The CSS/jQuery-style selector used to target element(s).transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Example setup
I saw a <span id="dog">dog</span>.
→ Prepend to the contents of the target element
<<link "Size">>
<<prepend "#dog">>big <</prepend>>
<</link>>
→ Result, after clicking
I saw a <span id="dog">big dog</span>.
→ Example setup
I saw a <span id="dog">dog</span>.
→ Prepend to the contents of the target element
<<link "Size">>
<<prepend "#dog" t8n>>big <</prepend>>
<</link>>
→ Result, after clicking
I saw a <span id="dog"><span class="macro-prepend-insert">big </span>dog</span>.
<<remove selector>>Removes the selected element(s).
SEE: DOM macro warning.
NOTE: If you simply want to empty the selected element(s), not remove them outright, you should use an empty <<replace>> instead.
v2.0.0selector: The CSS/jQuery-style selector used to target element(s).→ Given the following
I'd like a <span id="huge-cupcake">humongous </span>cupcake, please.
→ Remove the target element
<<link "Go small">>
<<remove "#huge-cupcake">>
<</link>>
→ Result, after the click
I'd like a cupcake, please.
<<removeclass selector [classNames]>>Removes classes from the selected element(s).
SEE: DOM macro warning.
v2.0.0selector: The CSS/jQuery-style selector used to target element(s).classNames: (optional) The names of the classes, separated by spaces. If no class names are given, removes all classes.<<removeclass "body" "day rain">> → Remove the classes "day" and "rain" from the <body> element
<<removeclass "#pie" "cherry">> → Remove the class "cherry" from the element with the ID "pie"
<<removeclass ".joe" "angry">> → Remove the class "angry" from all elements containing the class "joe"
<<removeclass "#begone">> → Remove all classes from the element with the ID "begone"
<<replace selector [transition|t8n]>> … <</replace>>Executes its contents and replaces the contents of the selected element(s) with the output.
SEE: DOM macro warning.
v2.0.0: Basic syntax.v2.25.0: Added transition and t8n keywords.selector: The CSS/jQuery-style selector used to target element(s).transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ Example setup
I saw a <span id="dog">dog</span>.
→ Replace the contents of the target element
<<link "Breed">>
<<replace "#dog">>Catahoula Cur<</replace>>
<</link>>
→ Result, after clicking
I saw a <span id="dog">Catahoula Cur</span>.
→ Example setup
I saw a <span id="dog">dog</span>.
→ Replace the contents of the target element
<<link "Breed">>
<<replace "#dog" t8n>>Catahoula Cur<</replace>>
<</link>>
→ Result, after clicking
I saw a <span id="dog"><span class="macro-replace-insert">Catahoula Cur</span></span>.
<<toggleclass selector classNames>>Toggles classes on the selected element(s) (i.e. adding them if they don't exist, removing them if they do).
SEE: DOM macro warning.
v2.0.0selector: The CSS/jQuery-style selector used to target element(s).classNames: The names of the classes, separated by spaces.<<toggleclass "body" "day rain">> → Toggle the classes "day" and "rain" on the <body> element
<<toggleclass "#pie" "cherry">> → Toggle the class "cherry" on the element with the ID "pie"
<<toggleclass ".joe" "angry">> → Toggle the class "angry" on all elements containing the class "joe"
The audio subsystem in SugarCube is based upon the HTML Media Elements API and comes with some built-in limitations:
<<timed>>—though this ultimately depends on various factors. A simple solution for the former is to use some kind of click/touch-through screen—e.g. a splash screen, which the player goes through to the real starting passage. The latter is harder to resolve, so is best avoided.
<<audio trackIdList actionList>>Controls the playback of audio tracks, which must be set up via <<cacheaudio>>.
SEE: Audio macro limitations.
NOTE: The <<audio>> macro cannot affect playlist tracks which have been copied into their respective playlist—meaning those set up via <<createplaylist>> with its copy action or all tracks set up via, the deprecated, <<setplaylist>>—as playlist copies are solely under the control of their playlist.
v2.0.0: Basic syntax.v2.1.0: Added fadeoverto action.v2.8.0: Added group ID(s).v2.28.0: Added load and unload actions.trackIdList: The list of track and/or group IDs, separated by spaces. See below for details on group IDs.actionList: The list of actions to perform. Available actions are:
fadein: Start playback of the selected tracks and fade them from their current volume level to 1 (loudest) over 5 seconds.fadeout: Start playback of the selected tracks and fade them from their current volume level to 0 (silent) over 5 seconds.fadeoverto seconds level: Start playback of the selected tracks and fade them from their current volume level to the specified level over the specified number of seconds.fadeto level: Start playback of the selected tracks and fade them from their current volume level to the specified level over 5 seconds.goto passage: Forwards the player to the passage with the given name when playback of the first of the selected tracks ends normally. May be called either with the passage name or with a link markup.load: Pause playback of the selected tracks and, if they're not already in the process of loading, force them to drop any existing data and begin loading. NOTE: This should not be done lightly if your audio sources are on the network, as it forces the player to begin downloading them.loop: Set the selected tracks to repeat playback upon ending.mute: Mute the volume of the selected tracks (effectively volume 0, except without changing the volume level).pause: Pause playback of the selected tracks.play: Start playback of the selected tracks.stop: Stop playback of the selected tracks.time seconds: Set the current playback time of the selected tracks to the specified number of seconds. Valid values are floating-point numbers in the range 0 (start) to the maximum duration (e.g. 60 is 60 is sixty seconds in, 90.5 is ninety-point-five seconds in).unload: Stop playback of the selected tracks and force them to drop any existing data. NOTE: Once unloaded, playback cannot occur until a load action is issued.unloop: Set the selected tracks to not repeat playback (this is the default).unmute: Unmute the volume of the selected tracks (this is the default).volume level: Set the volume of the selected tracks to the specified level. Valid values are floating-point numbers in the range 0 (silent) to 1 (loudest) (e.g. 0 is 0%, 0.5 is 50%, 1 is 100%).Group IDs allow several tracks to be selected simultaneously without needing to specify each one individually. There are several predefined group IDs (:all, :looped, :muted, :paused, :playing) and custom IDs may be defined via <<createaudiogroup>>. The :not() group modifier syntax (groupId:not(trackIdList)) allows a group to have some of its tracks excluded from selection.
→ Start playback of paused tracks
<<audio ":paused" play>>
→ Pause playback of playing tracks
<<audio ":playing" pause>>
→ Stop playback of playing tracks
<<audio ":playing" stop>>
→ Stop playback of all tracks (equivalent to <<stopallaudio>>)
<<audio ":all" stop>>
→ Stop playback of playing tracks except those in the ":ui" group
<<audio ":playing:not(:ui)" stop>>
→ Change the volume of all tracks except those in the ":ui" group
→ to 40%, without changing the current playback state
<<audio ":all:not(:ui)" volume 0.40>>
→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "bgm_space" "media/audio/space_quest.mp3" "media/audio/space_quest.ogg">>
→ Start playback
<<audio "bgm_space" play>>
→ Start playback at 50% volume
<<audio "bgm_space" volume 0.5 play>>
→ Start playback at 120 seconds in
<<audio "bgm_space" time 120 play>>
→ Start repeating playback
<<audio "bgm_space" loop play>>
→ Start playback and fade from 0% to 100% volume
<<audio "bgm_space" volume 0 fadein>>
→ Start playback and fade from 75% to 0% volume
<<audio "bgm_space" volume 0.75 fadeout>>
→ Start playback and fade from 25% to 75% volume
<<audio "bgm_space" volume 0.25 fadeto 0.75>>
→ Start playback and fade from 25% to 75% volume over 30 seconds
<<audio "bgm_space" volume 0.25 fadeoverto 30 0.75>>
→ Start playback and goto the "Peace Moon" passage upon ending normally
<<audio "bgm_space" play goto "Peace Moon">>
→ Pause playback
<<audio "bgm_space" pause>>
→ Stop playback
<<audio "bgm_space" stop>>
→ Mute playback, without changing the current playback state
<<audio "bgm_space" mute>>
→ Unmute playback, without changing the current playback state
<<audio "bgm_space" unmute>>
→ Change the volume to 40%, without changing the current playback state
<<audio "bgm_space" volume 0.40>>
→ Seek to 90 seconds in, without changing the current playback state
<<audio "bgm_space" time 90>>
load and unload actionsNOTE: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
→ If it's not currently loading, drop existing data buffers and load the track
<<audio "bgm_space" load>>
→ Unload the track, dropping existing data buffers
<<audio "bgm_space" unload>>
<<cacheaudio trackId sourceList>>Caches an audio track for use by the other audio macros.
NOTE: The StoryInit special passage is normally the best place to set up tracks.
v2.0.0: Basic syntax.v2.28.0: Deprecated the old optional format specifier syntax in favor of a new syntax (formatId|).trackId: The ID of the track, which will be used to reference it.sourceList: A space separated list of sources for the track. Only one is required, though supplying additional sources in differing formats is recommended, as no single format is supported by all browsers. A source must be either a URL (absolute or relative) to an audio resource, the name of an audio passage, or a data URI. In rare cases where the audio format cannot be automatically detected from the source (URLs are parsed for a file extension, data URIs are parsed for the media type), a format specifier may be prepended to the front of each source to manually specify the format (syntax: formatId|, where formatId is the audio format—generally, whatever the file extension would normally be; e.g. mp3, mp4, ogg, weba, wav).→ Cache a track with the ID "boom" and one source via relative URL
<<cacheaudio "boom" "media/audio/explosion.mp3">>
→ Cache a track with the ID "boom" and one source via audio passage
<<cacheaudio "boom" "explosion">>
→ Cache a track with the ID "bgm_space" and two sources via relative URLs
<<cacheaudio "bgm_space" "media/audio/space_quest.mp3" "media/audio/space_quest.ogg">>
→ Cache a track with the ID "what" and one source via URL with a format specifier
<<cacheaudio "what" "mp3|http://an-audio-service.com/a-user/a-track-id">>
<<createaudiogroup groupId>> [<<track trackId>> …] <</createaudiogroup>>Collects tracks, which must be set up via <<cacheaudio>>, into a group via its <<track>> children. Groups are useful for applying actions to multiple tracks simultaneously and/or excluding the included tracks from a larger set when applying actions.
NOTE: The StoryInit special passage is normally the best place to set up groups.
v2.19.0<<createaudiogroup>>groupId: The ID of the group, which will be used to reference it and must begin with a colon. NOTE: There are several predefined group IDs (:all, :looped, :muted, :paused, :playing) and the :not group modifier which cannot be reused/overwritten.<<track>>trackId: The ID of the track.→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "ui_beep" "media/audio/ui/beep.mp3">>
<<cacheaudio "ui_boop" "media/audio/ui/boop.mp3">>
<<cacheaudio "ui_swish" "media/audio/ui/swish.mp3">>
→ Set up a group ":ui" with the tracks: "ui_beep", "ui_boop", and "ui_swish"
<<createaudiogroup ":ui">>
<<track "ui_beep">>
<<track "ui_boop">>
<<track "ui_swish">>
<</createaudiogroup>>
<<createplaylist listId>> [<<track trackId actionList>> …] <</createplaylist>>Collects tracks, which must be set up via <<cacheaudio>>, into a playlist via its <<track>> children.
NOTE: The StoryInit special passage is normally the best place to set up playlists.
v2.8.0<<createplaylist>>listId: The ID of the playlist, which will be used to reference it.<<track>>trackId: The ID of the track.actionList: The list of actions to perform. Available actions are:
volume level: (optional) Set the base volume of the track within the playlist to the specified level. If omitted, defaults to the track's current volume. Valid values are floating-point numbers in the range 0 (silent) to 1 (loudest) (e.g. 0 is 0%, 0.5 is 50%, 1 is 100%).copy: (optional) Keyword, used to signify that the playlist should create its own independent copy of the track, rather than simply referencing the existing version. Copies are solely under the control of their playlist—meaning <<audio>> actions cannot affect them, even when using group IDs.→ Given the following setup (best done in the StoryInit special passage)
<<cacheaudio "swamped" "media/audio/Swamped.mp3">>
<<cacheaudio "heavens_a_lie" "media/audio/Heaven's_A_Lie.mp3">>
<<cacheaudio "closer" "media/audio/Closer.mp3">>
<<cacheaudio "to_the_edge" "media/audio/To_The_Edge.mp3">>
→ Create a playlist "bgm_lacuna" with the tracks: "swamped", "heavens_a_lie", "closer", and "to_the_edge"
<<createplaylist "bgm_lacuna">>
<<track "swamped" volume 1>> → Add "swamped" at 100% volume
<<track "heavens_a_lie" volume 0.5>> → Add "heavens_a_lie" at 50% volume
<<track "closer" copy>> → Add a copy of "closer" at its current volume
<<track "to_the_edge" volume 1 copy>> → Add a copy of "to_the_edge" at 100% volume
<</createplaylist>>
<<masteraudio actionList>>Controls the master audio settings.
SEE: Audio macro limitations.
v2.8.0: Basic syntax.v2.28.0: Added load, muteonhide, nomuteonhide, and unload actions.actionList: The list of actions to perform. Available actions are:
load: Pause playback of all tracks and, if they're not already in the process of loading, force them to drop any existing data and begin loading. NOTE: This should not be done lightly if your audio sources are on the network, as it forces the player to begin downloading them.mute: Mute the master volume (effectively volume 0, except without changing the volume level).muteonhide: Enable automatic muting of the master volume when losing visibility—i.e. when switched to another tab or the browser window is minimized.nomuteonhide: Disable automatic muting of the master volume when losing visibility (this is the default).stop: Stop playback of all tracks.unload: Stop playback of all tracks and force them to drop any existing data. NOTE: Once unloaded, playback cannot occur until a load action is issued for each track—either a master load action, to affect all tracks, or an <<audio>>/<<playlist>> load action, to affect only certain tracks.unmute: Unmute the master volume (this is the default).volume level: Set the master volume to the specified level. Valid values are floating-point numbers in the range 0 (silent) to 1 (loudest) (e.g. 0 is 0%, 0.5 is 50%, 1 is 100%).→ Stop playback of all registered tracks, no exceptions
<<masteraudio stop>>
→ Change the master volume to 40%
<<masteraudio volume 0.40>>
→ Mute the master volume
<<masteraudio mute>>
→ Unmute the master volume
<<masteraudio unmute>>
→ Enable automatic muting of the master volume when losing visibility
<<masteraudio muteonhide>>
→ Disable automatic muting of the master volume when losing visibility
<<masteraudio nomuteonhide>>
load and unload actionsNOTE: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
→ If they're not currently loading, drop existing data buffers and load all tracks
<<masteraudio load>>
→ Unload all tracks, dropping existing data buffers
<<masteraudio unload>>
<<playlist listId actionList>><<playlist actionList>>Controls the playback of the playlist, which must be set up via <<createplaylist>>—the deprecated <<setplaylist>> may be used instead, though it is not recommended.
SEE: Audio macro limitations.
v2.0.0: Basic syntax, compatible with <<setplaylist>>.v2.1.0: Added fadeoverto action.v2.8.0: Added listId argument, compatible with <<createplaylist>>. Deprecated <<setplaylist>> compatible form.v2.28.0: Added load and unload actions.<<createplaylist>>-compatible formlistId: The ID of the playlist.actionList: The list of actions to perform. Available actions are:
fadein: Start playback of the playlist and fade the current track from its current volume level to 1 (loudest) over 5 seconds.fadeout: Start playback of the playlist and fade the current track from its current volume level to 0 (silent) over 5 seconds.fadeoverto seconds level: Start playback of the playlist and fade the current track from its current volume level to the specified level over the specified number of seconds.fadeto level: Start playback of the playlist and fade the current track from its current volume level to the specified level over 5 seconds.load: Pause playback of the playlist and, if its tracks are not already in the process of loading, force them to drop any existing data and begin loading. NOTE: This should not be done lightly if your audio sources are on the network, as it forces the player to begin downloading them.loop: Set the playlist to repeat playback upon ending.mute: Mute the volume of the playlist (effectively volume 0, except without changing the volume level).pause: Pause playback of the playlist.play: Start playback of the playlist.shuffle: Set the playlist to randomly shuffle.skip: Skip ahead to the next track in the queue. An empty queue will not be refilled unless repeat playback has been set.stop: Stop playback of the playlist.unload: Stop playback of the playlist and force its tracks to drop any existing data. NOTE: Once unloaded, playback cannot occur until a load action is issued.unloop: Set the playlist to not repeat playback (this is the default).unmute: Unmute the volume of the playlist (this is the default).unshuffle: Set the playlist to not randomly shuffle (this is the default).volume level: Set the volume of the playlist to the specified level. Valid values are floating-point numbers in the range 0 (silent) to 1 (loudest) (e.g. 0 is 0%, 0.5 is 50%, 1 is 100%).<<setplaylist>>-compatible formactionList: Identical to the <<createplaylist>>-compatible form.<<createplaylist>>-compatible form shown)→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "swamped" "media/audio/Swamped.mp3">>
<<cacheaudio "heavens_a_lie" "media/audio/Heaven's_A_Lie.mp3">>
<<cacheaudio "closer" "media/audio/Closer.mp3">>
<<cacheaudio "to_the_edge" "media/audio/To_The_Edge.mp3">>
<<createplaylist "bgm_lacuna">>
<<track "swamped" volume 1>>
<<track "heavens_a_lie" volume 1>>
<<track "closer" volume 1>>
<<track "to_the_edge" volume 1>>
<</createplaylist>>
→ Start playback
<<playlist "bgm_lacuna" play>>
→ Start playback at 50% volume
<<playlist "bgm_lacuna" volume 0.5 play>>
→ Start non-repeating playback
<<playlist "bgm_lacuna" unloop play>>
→ Start playback with a randomly shuffled playlist
<<playlist "bgm_lacuna" shuffle play>>
→ Start playback and fade from 0% to 100% volume
<<playlist "bgm_lacuna" volume 0 fadein>>
→ Start playback and fade from 75% to 0% volume
<<playlist "bgm_lacuna" volume 0.75 fadeout>>
→ Start playback and fade from 25% to 75% volume
<<playlist "bgm_lacuna" volume 0.25 fadeto 0.75>>
→ Start playback and fade from 25% to 75% volume over 30 seconds
<<playlist "bgm_lacuna" volume 0.25 fadeoverto 30 0.75>>
→ Pause playback
<<playlist "bgm_lacuna" pause>>
→ Stop playback
<<playlist "bgm_lacuna" stop>>
→ Mute playback, without changing the current playback state
<<playlist "bgm_lacuna" mute>>
→ Unmute playback, without changing the current playback state
<<playlist "bgm_lacuna" unmute>>
→ Change the volume to 40%, without changing the current playback state
<<playlist "bgm_lacuna" volume 0.40>>
→ Set the playlist to randomly shuffle, without changing the current playback state
<<playlist "bgm_lacuna" shuffle>>
load and unload actionsNOTE: Be very careful with these if your audio sources are on the network, as you are forcing players to begin downloading them. Not everyone has blazing fast internet with unlimited data—especially true for mobile users. Pease, do not take your players' bandwidth and data usage lightly.
→ If they're not currently loading, drop existing data buffers and load all of the playlist's tracks
<<playlist "bgm_lacuna" load>>
→ Unload all of the playlist's tracks, dropping existing data buffers
<<playlist "bgm_lacuna" unload>>
<<removeaudiogroup groupId>>Removes the audio group with the given ID.
NOTE: You may not remove the predefined group IDs (:all, :looped, :muted, :paused, :playing) or the :not group modifier.
v2.28.0groupId: The ID of the group.→ Given a group set up via <<createaudiogroup ":ui">>…<</createplaylist>>
<<removeaudiogroup ":ui">>
<<removeplaylist listId>>Removes the playlist with the given ID.
v2.8.0listId: The ID of the playlist.→ Given a playlist set up via <<createplaylist "bgm_lacuna">>…<</createplaylist>>
<<removeplaylist "bgm_lacuna">>
<<waitforaudio>>Displays the loading screen until all currently registered audio has either loaded to a playable state or aborted loading due to errors. Requires tracks to be set up via <<cacheaudio>>.
NOTE: This macro should be invoked once, following any invocations of <<cacheaudio>> and, if any <<track>> definitions used the copy keyword, <<createplaylist>> for which you want the loading screen displayed.
v2.8.0<<cacheaudio "a" "a_track.…">>
<<cacheaudio "b" "b_track.…">>
<<cacheaudio "c" "c_track.…">>
<<cacheaudio "d" "d_track.…">>
<<waitforaudio>>
→ First, register the tracks that will be needed soon
<<cacheaudio "a" "a_track.…">>
<<cacheaudio "b" "b_track.…">>
→ Next, load all currently registered tracks (meaning: "a" and "b")
<<waitforaudio>>
→ Finally, register any tracks that won't be needed until later
<<cacheaudio "c" "c_track.…">>
<<cacheaudio "d" "d_track.…">>
<<setplaylist trackIdList>>Deprecated:
This macro has been deprecated and should no longer be used. See the <<createplaylist>> macro for its replacement.
Collects audio tracks, which must be set up via <<cacheaudio>>, into a playlist by making its own independent copies of the tracks, rather than simply referencing the existing versions. Copies are solely under the control of the playlist—meaning <<audio>> actions cannot affect them, even when using group IDs.
NOTE: The StoryInit special passage is normally the best place to set up playlists.
v2.0.0: Basic syntax.v2.8.0: Deprecated in favor of <<createplaylist>>.trackIdList: A space separated list of track ID(s) for the playlist (only one is required).→ Given the following (best done in the StoryInit special passage)
<<cacheaudio "swamped" "media/audio/Swamped.mp3">>
<<cacheaudio "heavens_a_lie" "media/audio/Heaven's_A_Lie.mp3">>
<<cacheaudio "closer" "media/audio/Closer.mp3">>
<<cacheaudio "to_the_edge" "media/audio/To_The_Edge.mp3">>
→ Set up the playlist with the tracks: "swamped", "heavens_a_lie", "closer", and "to_the_edge"
<<setplaylist "swamped" "heavens_a_lie" "closer" "to_the_edge">>
<<stopallaudio>>Deprecated:
This macro has been deprecated and should no longer be used. See the <<audio>> macro for its replacement.
Immediately stops the playback of all tracks, which must be set up via <<cacheaudio>>.
NOTE: Does not affect playlist tracks which have been copied into their respective playlist—meaning those set up via <<createplaylist>> with its copy action or all tracks set up via, the deprecated, <<setplaylist>>.
v2.0.0: Basic syntax.v2.8.0: Deprecated in favor of <<audio ":all" stop>>.<<stopallaudio>>
<<goto passageName>><<goto linkMarkup>>Immediately forwards the player to the passage with the given name. May be called either with the passage name or with a link markup.
NOTE: In most cases, you will not need to use <<goto>> as there are often better and easier ways to forward the player. For example, a common use of <<link>> is to perform various actions before forwarding the player to another passage. In that case, unless you need to dynamically determine the destination passage within the <<link>> body, <<goto>> is unnecessary as <<link>> already includes the ability to forward the player.
WARNING: <<goto>> does not terminate the rendering passage in which it was encountered, so care must be taken to ensure that no unwanted state modifications occur after the call to <<goto>>.
v2.0.0passageName: The name of the passage to go to.linkMarkup: The link markup to use (regular syntax only, no setters).→ Passage name form
<<goto "Somewhere over yonder">>
<<goto $selectedPassage>>
→ Link markup form
<<goto [[Somewhere over yonder]]>>
<<goto [[$selectedPassage]]>>
<<repeat delay [transition|t8n]>> … <</repeat>>Repeatedly executes its contents after the given delay, inserting any output into the passage in its place. May be terminated by a <<stop>> macro.
NOTE: Passage navigation terminates all pending timed executions.
v2.0.0delay: The amount of time to delay, as a valid CSS time value (e.g. 5s and 500ms). The minimum delay is 40ms.transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.→ A countdown timer
<<set $seconds to 10>>\
Countdown: <span id="countdown">$seconds seconds remaining</span>!\
<<silently>>
<<repeat 1s>>
<<set $seconds to $seconds - 1>>
<<if $seconds gt 0>>
<<replace "#countdown">>$seconds seconds remaining<</replace>>
<<else>>
<<replace "#countdown">>Too Late<</replace>>
/* do something useful here */
<<stop>>
<</if>>
<</repeat>>
<</silently>>
<<stop>>Used within <<repeat>> macros. Terminates the execution of the current <<repeat>>.
v2.0.0
<<timed delay [transition|t8n]>> … [<<next [delay]>> …] <</timed>>Executes its contents after the given delay, inserting any output into the passage in its place. Additional timed executions may be chained via <<next>>.
NOTE: Passage navigation terminates all pending timed executions.
v2.0.0<<timed>>delay: The amount of time to delay, as a valid CSS time value (e.g. 5s and 500ms). The minimum delay is 40ms.transition: (optional) Keyword, used to signify that a CSS transition should be applied to the incoming insertions.t8n: (optional) Keyword, alias for transition.<<next>>delay: (optional) The amount of time to delay, as a valid CSS time value (e.g. 5s and 500ms). The minimum delay is 40ms. If omitted, the last delay specified, from a <<next>> or the parent <<timed>>, will be used.→ Insert some text after 5 seconds with a transition
I want to go to…<<timed 5s t8n>> WONDERLAND!<</timed>>
→ Replace some text after 10 seconds
I like green <span id="eggs">eggs</span> and ham!\
<<timed 10s>><<replace "#eggs">>pancakes<</replace>><</timed>>
→ A execute <<goto>> after 10 seconds
<<timed 10s>><<goto "To the Moon, Alice">><</timed>>
→ Insert some text in 2 second intervals three times (at: 2s, 4s, 6s)
<<timed 2s>>Hi! Ho!
<<next>>Hi! Ho!
<<next>>It's off to work we go!
<</timed>>
→ Replace some text in 1 second intervals
I'll have <span id="drink">some water</span>, please.\
<<timed 1s>><<replace "#drink">>a glass of milk<</replace>>\
<<next>><<replace "#drink">>a can of soda<</replace>>\
<<next>><<replace "#drink">>a cup of coffee<</replace>>\
<<next>><<replace "#drink">>tea, southern style, sweet<</replace>>\
<<next>><<replace "#drink">>a scotch, neat<</replace>>\
<<next>><<replace "#drink">>a bottle of your finest absinthe<</replace>>\
<</timed>>
→ Set a $variable after 4 seconds, 3 seconds, 2 seconds, and 1 second
<<silently>>
<<set $choice to 0>>
<<timed 4s>>
<<set $choice to 1>>
<<next 3s>>
<<set $choice to 2>>
<<next 2s>>
<<set $choice to 3>>
<<next 1s>>
<<set $choice to 4>>
<</timed>>
<<silently>>
<<widget widgetName>> … <</widget>>Creates a new widget macro (henceforth, widget) with the given name. Widgets allow you to create macros by using the standard macros and markup that you use normally within your story. Widgets may access arguments passed to them via the $args array (indices are zero-based: $args[0] is the first parsed argument, $args[1] is the second, etc.). Additionally, the full argument string (in raw and parsed forms) may be accessed via the $args.raw and $args.full properties.
WARNING: Widgets should always be defined within their own separate widget-tagged passage—any widgets that are not may be lost on page reload. Do not add a widget tag to any of the specially named passages and attempt to define your widgets there.
v2.0.0$args variable is used internally to store passed arguments (and the full argument string). When a widget is called, any existing $args variable is stored for the duration of the call and restored after. This means that non-widget use of an $args variable is safe (n.b. this does have the effect that an $args variable external to a widget is inaccessible to it unless passed in as an argument).widgetName: The name of the created widget; it should not contain whitespace or angle brackets (i.e. less-than "<" or greater-than ">"). If the name of an existing widget is chosen, the new widget will overwrite the older version. The names of existing standard macros are invalid widget names, so you cannot overwrite standard macros, and any attempts to do so will cause an error.→ Creating a gender pronoun widget
<<widget "he">><<if $pcSex eq "male">>he<<elseif $pcSex eq "female">>she<<else>>it<</if>><</widget>>
→ Using it
"Are you sure that <<he>> can be trusted?"
→ Creating a silly print widget
<<widget "pm">><<if $args[0]>><<print $args[0]>><<else>>Mum's the word!<</if>><</widget>>
→ Using it
<<pm>> → Outputs: Mum's the word!
<<pm "Hi!">> → Outputs: Hi!
clone(original) → anyReturns a deep copy of the given value.
NOTE: Only the primitives, generic objects, some JavaScript natives (specifically: Array, Date, Map, RegExp, and Set), and DOM node objects are supported by default. Unsupported objects will need a .clone() method to be properly supported by the cone() function—when called on such an object, it will simply defer to the local method.
v2.0.0original: (any) The object to value.// Without clone(); given the generic object: $foo = { id : 1 }
<<set $bar to $foo>>
<<set $bar.id to 5>>
$foo.id → Returns: 5
$bar.id → Returns: 5
// With clone(); given the generic object: $foo = { id : 1 }
<<set $bar to clone($foo)>>
<<set $bar.id to 5>>
$foo.id → Returns: 1
$bar.id → Returns: 5
either(list…) → anyReturns a random value from its given arguments.
v2.0.0list: (any) The list of values to operate on. May be any combination of singular values, actual arrays, or array-like objects. All values will be concatenated into a single list for selection. NOTE: Does not flatten nested arrays—if this is required, the <Array>.flatten() method may be used to flatten the nested arrays prior to passing them to either().// Using singular values
either("Blueberry", "Cherry", "Pecan") → Returns a random pie from the whole list
// Using arrays; given: $pies = ["Blueberry", "Cherry", "Pecan"]
either($pies) → Returns a random pie from the whole array
// Using singular values and arrays; given: $letters = ["A", "B"]
either($letters, "C", "D") → Returns a random value from the whole list (i.e. "A", "B", "C", "D")
// Using multiple arrays; given: $letters = ["A", "B"] & $numerals = ["1", "2"]
either($letters, $numerals) → Returns a random value from the whole list (i.e. "A", "B", "1", "2")
hasVisited(passages…) → booleanReturns whether the passage with the given title occurred within the story history. If multiple passage titles are given, returns the logical-AND aggregate of the set (i.e. true if all were found, false if any were not found).
v2.7.0passages: (string | string array) The title(s) of the passage(s) to search for. May be a list or an array of passages.<<if hasVisited("Bar")>>…has been to the Bar…<</if>>
<<if not hasVisited("Bar")>>…has never been to the Bar…<</if>>
<<if hasVisited("Bar", "Café")>>…has been to both the Bar and Café<</if>>
<<if not hasVisited("Bar", "Café")>>…has never been to either the Bar, Café, or both…<</if>>
lastVisited(passages…) → integerReturns the number of turns that have passed since the last instance of the passage with the given title occurred within the story history or -1 if it does not exist. If multiple passage titles are given, returns the lowest count (which can be -1).
v2.0.0passages: (string | string array) The title(s) of the passage(s) to search for. May be a list or an array of passages.<<if lastVisited("Bar") is -1>>…has never been to the Bar…<</if>>
<<if lastVisited("Bar") is 0>>…is currently in the Bar…<</if>>
<<if lastVisited("Bar") is 1>>…was in the Bar one turn ago…<</if>>
<<if lastVisited("Bar", "Café") is -1>>…has never been to the Bar, Café, or both…<</if>>
<<if lastVisited("Bar", "Café") is 2>>…has been to both the Bar and Café, most recently two turns ago…<</if>>
importScripts(urls…) → Promise objectLoad and integrate external JavaScript scripts.
NOTE: Loading is done asynchronously at run time, so if the script must be available within a tight time frame, then you should use the Promise returned by the function to ensure the script is loaded before before it is needed.
NOTE: A script section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) is normally the best place to call importScripts().
v2.16.0urls: (string | string array) The URLs of the external scripts to import. Loose URLs are imported concurrently, arrays of URLs are imported sequentially.// Import all scripts concurrently
importScripts(
"https://somesite/a/path/a.js",
"https://somesite/a/path/b.js",
"https://somesite/a/path/c.js",
"https://somesite/a/path/d.js"
);
// Import all scripts sequentially
importScripts([
"https://somesite/a/path/a.js",
"https://somesite/a/path/b.js",
"https://somesite/a/path/c.js",
"https://somesite/a/path/d.js"
]);
// Import scripts a.js, b.js, and the c.js/d.js group concurrently,
// while importing c.js and d.js sequentially relative to each other
importScripts(
"https://somesite/a/path/a.js",
"https://somesite/a/path/b.js",
[
"https://somesite/a/path/c.js",
"https://somesite/a/path/d.js"
]
);
Promise object// Import a script while using the returned Promise to ensure that
// the script has been fully loaded before executing dependent code
importScripts("https://somesite/a/path/a.js")
.then(function () {
// Code that depends on the script goes here.
})
.catch(function (err) {
// There was an error loading the script, log it to the console.
console.log(err);
});
Promise object for later use// Import a script while saving the returned Promise so it may be used later
setup.aScriptImport = importScripts("https://somesite/a/path/aScript.js");
// Use the returned Promise later on to ensure that the script has been fully
// loaded before executing dependent code
setup.aScriptImport
.then(function () {
// Code that depends on the script goes here.
})
.catch(function (err) {
// There was an error loading the script, log it to the console.
console.log(err);
});
importStyles(urls…) → Promise objectLoad and integrate external CSS stylesheets.
NOTE: Loading is done asynchronously at run time, so if the stylesheet must be available within a tight time frame, then you should use the Promise returned by the function to ensure the stylesheet is loaded before it is needed.
NOTE: A script section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage) is normally the best place to call importStyles().
v2.16.0urls: (string | string array) The URLs of the external stylesheets to import. Loose URLs are imported concurrently, arrays of URLs are imported sequentially.// Import all stylesheets concurrently
importStyles(
"https://somesite/a/path/a.css",
"https://somesite/a/path/b.css",
"https://somesite/a/path/c.css",
"https://somesite/a/path/d.css"
);
// Import all stylesheets sequentially
importStyles([
"https://somesite/a/path/a.css",
"https://somesite/a/path/b.css",
"https://somesite/a/path/c.css",
"https://somesite/a/path/d.css"
]);
// Import stylesheets a.css, b.css, and the c.css/d.css group concurrently,
// while importing c.css and d.css sequentially relative to each other
importStyles(
"https://somesite/a/path/a.css",
"https://somesite/a/path/b.css",
[
"https://somesite/a/path/c.css",
"https://somesite/a/path/d.css"
]
);
Promise object// Grab a loading screen lock
var lsLockId = LoadScreen.lock();
// Import a stylesheet while using the returned Promise to ensure that the
// stylesheet has been fully loaded before unlocking the loading screen
importStyles("https://somesite/a/path/a.css")
.then(function () {
// The stylesheet has been loaded, release the loading screen lock.
LoadScreen.unlock(lsLockId);
})
.catch(function (err) {
// There was an error loading the stylesheet, log it to the console.
console.log(err);
});
passage() → stringReturns the title of the active (present) passage.
v2.0.0<<if passage() is "Café">>…the current passage is the Café passage…<</if>>
previous() → stringReturns the title of the most recent previous passage whose title does not match that of the active passage or an empty string, if there is no such passage.
v2.0.0<<if previous() is "Café">>…the most recent non-active passage is the Café passage…<</if>>
→ Commonly used as part of a link to return to the most recent non-active passage
[[Return|previous()]]
random([min ,] max) → integerReturns a pseudo-random whole number (integer) within the range of the given bounds (inclusive)—i.e. [min, max].
NOTE: By default, it uses Math.random() as its source of randomness, however, when the seedable PRNG has been enabled, via State.initPRNG(), it uses the seeded PRNG instead.
v2.0.0min: (optional) The lower bound of the random number (inclusive). If omitted, will default to 0.max: The upper bound of the random number (inclusive).random(5) → Returns a number in the range 0–5
random(1, 6) → Returns a number in the range 1–6
randomFloat([min ,] max) → floatReturns a pseudo-random real number (floating-point) within the range of the given bounds (inclusive for the minimum, exclusive for the maximum)—i.e. [min, max).
NOTE: By default, it uses Math.random() as its source of randomness, however, when the seedable PRNG has been enabled, via State.initPRNG(), it uses the seeded PRNG instead.
v2.0.0min: (optional) The lower bound of the random number (inclusive). If omitted, will default to 0.0.max: The upper bound of the random number (exclusive).randomFloat(5.0) → Returns a number in the range 0.0–4.9999999…
randomFloat(1.0, 6.0) → Returns a number in the range 1.0–5.9999999…
setPageElement(idOrElement , passages [, defaultText]) → HTMLElement object | nullRenders the selected passage into the target element, replacing any existing content, and returns the element. If no passages are found and default text is specified, it will be used instead.
v2.0.0idOrElement: (string | HTMLElement object) The ID of the element or the element itself.passages: (string | string array) The name(s) of the passage(s) to search for. May be a single passage or an array of passages. If an array of passage names is specified, the first passage to be found is used.defaultText: (optional, string) The default text to use if no passages are found.NOTE: As it is highly unlikely that either an array of passage names or default text will be needed in the vast majority of cases, only a few basic examples will be given.
// Using an ID; given an existing element on the page: <div id="my-display"></div>
setPageElement("my-display", "MyPassage");
// Using an element; given a reference to an existing element: myElement
setPageElement(myElement, "MyPassage");
tags([passages…]) → string arrayReturns a new array consisting of all of the tags of the given passages.
v2.0.0passages: (optional, string | string array) The passages from which to collect tags. May be a list or an array of passages. If omitted, will default to the current passage.<<if tags().includes("forest")>>…the current passage is part of the forest…<</if>>
<<if tags("Lonely Glade").includes("forest")>>…the Lonely Glade passage is part of the forest…<</if>>
temporary() → objectReturns a reference to the current temporary variables store (equivalent to: State.temporary). This is only really useful within pure JavaScript code, as within TwineScript you may simply access temporary variables natively.
v2.19.0// Given: _selection is 'Zagnut Bar'
if (temporary().selection === 'Zagnut Bar') {
/* Do something… */
}
time() → integerReturns the number of milliseconds which have passed since the current passage was rendered to the page.
v2.0.0→ Links which vary based on the time
In the darkness, something wicked this way comes. Quickly! Do you \
<<link "try to run back into the light">>
<<if time() lt 5000>>
/% The player clicked the link in under 5s, so they escape %/
<<goto "Well lit passageway">>
<<else>>
/% Else, they're eaten by a grue %/
<<goto "Eaten by a grue">>
<</if>>
<</link>> \
or [[stand your ground|Eaten by a grue]]?
turns() → integerReturns the number of passages that the player has visited.
v2.0.0<<print "This is turn #" + turns()>>
variables() → objectReturns a reference to the active (present) story variables store (equivalent to: State.variables). This is only really useful within pure JavaScript code, as within TwineScript you may simply access story variables natively.
v2.0.0// Given: $hasGoldenKey is true
if (variables().hasGoldenKey) {
/* Do something… */
}
visited([passages…]) → integerReturns the number of times that the passage with the given title occurred within the story history. If multiple passage titles are given, returns the lowest count.
v2.0.0passages: (optional, string | string array) The title(s) of the passage(s) to search for. May be a list or an array of passages. If omitted, will default to the current passage.<<if visited() is 3>>…this is the third visit to the current passage…<</if>>
<<if visited("Bar")>>…has been to the Bar at least once…<</if>>
<<if visited("Café") is 1>>…has been to the Café exactly once…<</if>>
<<if visited("Bar", "Café") is 4>>…has been to both the Bar and Café at least four times…<</if>>
visitedTags(tags…) → integerReturns the number of passages within the story history which are tagged with all of the given tags.
v2.0.0tags: (string | string array) The tags to search for. May be a list or an array of tags.<<if visitedTags("forest")>>…has been to some part of the forest at least once…<</if>>
<<if visitedTags("forest", "haunted") is 1>>…has been to the haunted part of the forest exactly once…<</if>>
<<if visitedTags("forest", "burned") is 3>>…has been to the burned part of the forest three times…<</if>>
Most of the methods listed below are SugarCube extensions, with the rest being either JavaScript natives or bundled library methods which are listed here for their utility—though, this is not an exhaustive list.
For more information see:
Additionally. SugarCube includes polyfills for virtually all JavaScript (ECMAScript) 5 & 6 native object methods, so they may be safely used even if your project will be used in older browsers which do not natively support them.
<Array>.concatUnique(members…) → arrayConcatenates one or more unique members to the end of the base array and returns the result as a new array. Does not modify the original.
v2.21.0members: (any) The members to concatenate. Members which are arrays will be merged—i.e. their members will be concatenated, rather than the array itself.// Given: $fruits1 = ["Apples", "Oranges"], $fruits2 = ["Pears", "Plums"]
$fruits1.concatUnique($fruits2) → Returns ["Apples", "Oranges", "Pears", "Plums"]
$fruits1.concatUnique($fruits2, $fruits2) → Returns ["Apples", "Oranges", "Pears", "Plums"]
$fruits1.concatUnique("Pears") → Returns ["Apples", "Oranges", "Pears"]
$fruits1.concatUnique("Pears", "Pears") → Returns ["Apples", "Oranges", "Pears"]
$fruits1.concatUnique($fruits2, "Pears") → Returns ["Apples", "Oranges", "Pears", "Plums"]
<Array>.count(needle [, position]) → integerReturns the number of times that the given member was found within the array, starting the search at position.
v2.0.0needle: (any) The member to count.position: (optional, integer) The zero-based index at which to begin searching for needle. If omitted, will default to 0.// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.count("Oranges") → Returns 2
$fruits.count("Oranges", 2) → Returns 1
<Array>.delete(needles…) → arrayRemoves all instances of the given members from the array and returns a new array containing the removed members.
v2.5.0needles: (any | array) The members to remove. May be a list of members or an array.// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.delete("Oranges") → Returns ["Oranges", "Oranges"]; $fruits ["Apples", "Plums"]
$fruits.delete("Apples", "Plums") → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]
<Array>.deleteAt(indices…) → arrayRemoves all of the members at the given indices from the array and returns a new array containing the removed members.
v2.5.0indices: (integer | array) The indices of the members to remove. May be a list of indices or an array.// Given: $fruits = ["Apples", "Oranges", "Plums", "Oranges"]
$fruits.deleteAt(2) → Returns ["Plums"]; $fruits ["Apples", "Oranges", "Oranges"]
$fruits.deleteAt(1, 3) → Returns ["Oranges", "Oranges"]; $fruits ["Apples", "Plums"]
$fruits.deleteAt(0, 2) → Returns ["Apples", "Plums"]; $fruits ["Oranges", "Oranges"]
<Array>.deleteWith(predicate [, thisArg]) → arrayRemoves all of the members that pass the test implemented by the given predicate function from the array and returns a new array containing the removed members.
v2.25.0predicate: (function) The function used to test each member. It is called with three arguments:
value: (any) The member being processed.index: (optional, integer) The index of member being processed.array: (optional, array) The array being processed.thisArg: (optional, any) The value to use as this when executing predicate.// Given: $fruits = ["Apples", "Apricots", "Oranges"]
→ Returns ["Apricots"]; $fruits ["Apples", "Oranges"]
$fruits.deleteWith(function (val) {
return val === "Apricots";
})
→ Returns ["Apples", "Apricots"]; $fruits ["Oranges"]
$fruits.deleteWith(function (val) {
return val.startsWith("Ap");
})
// Given: $fruits = [{ name : "Apples" }, { name : "Apricots" }, { name : "Oranges" }]
→ Returns [{ name : "Apricots" }]; $fruits [{ name : "Apples" }, { name : "Oranges" }]
$fruits.deleteWith(function (val) {
return val.name === "Apricots";
})
→ Returns [{ name : "Apples" }, { name : "Apricots" }]; $fruits [{ name : "Oranges" }]
$fruits.deleteWith(function (val) {
return val.name.startsWith("Ap");
})
<Array>.first() → anyReturns the first member from the array. Does not modify the original.
v2.27.0// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.first() → Returns "Blueberry"
<Array>.flatten() → arrayReturns a new array consisting of the flattened source array (i.e. flat map reduce). Does not modify the original.
v2.0.0// Given: $npa = [["Alfa", "Bravo"], [["Charlie", "Delta"], ["Echo"]], "Foxtrot"]
$npa.flatten() → Returns ["Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"]
<Array>.includes(needle [, position]) → booleanReturns whether the given member was found within the array, starting the search at position.
needle: (any) The member to find.position: (optional, integer) The zero-based index at which to begin searching for needle. If omitted, will default to 0.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includes("Cherry")>>…found Cherry pie…<</if>>
<<if $pies.includes("Pecan", 3)>>…found Pecan pie within ["Pecan", "Pumpkin"]…<</if>>
<Array>.includesAll(needles…) → booleanReturns whether all of the given members were found within the array.
v2.10.0needles: (any | array) The members to find. May be a list of members or an array.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includesAll("Cherry", "Pecan")>>…found Cherry and Pecan pies…<</if>>
// Given: $search = ["Blueberry", "Pumpkin"]
<<if $pies.includesAll($search)>>…found Blueberry and Pumpkin pies…<</if>>
<Array>.includesAny(needles…) → booleanReturns whether any of the given members were found within the array.
v2.10.0needles: (any | array) The members to find. May be a list of members or an array.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
<<if $pies.includesAny("Cherry", "Pecan")>>…found Cherry or Pecan pie…<</if>>
// Given: $search = ["Blueberry", "Pumpkin"]
<<if $pies.includesAny($search)>>…found Blueberry or Pumpkin pie…<</if>>
<Array>.last() → anyReturns the last member from the array. Does not modify the original.
v2.27.0// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.last() → Returns "Pumpkin"
<Array>.pluck() → anyRemoves and returns a random member from the array.
v2.0.0// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.pluck() → Removes and returns a random pie from the array
<Array>.pluckMany(want) → arrayRandomly removes the given number of members from the base array and returns the removed members as a new array.
v2.20.0want: (optional, integer) The number of members to pluck.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.pluckMany(3) → Removes three random pies from the array and returns them as a new array
<Array>.pushUnique(members…) → numberAppends one or more unique members to the end of the base array and returns its new length.
v2.21.0members: (any) The members to append.// Given: $fruits = ["Apples", "Oranges"]
$fruits.pushUnique("Apples") → Returns 2; $fruits ["Apples", "Oranges"]
$fruits.pushUnique("Plums", "Plums") → Returns 3; $fruits ["Apples", "Oranges", "Plums"]
<Array>.random() → anyReturns a random member from the array. Does not modify the original.
v2.0.0// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.random() → Returns a random pie from the array
<Array>.randomMany(want) → arrayRandomly selects the given number of unique members from the array and returns the selected members as a new array. Does not modify the original.
v2.20.0want: (optional, integer) The number of members to select.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.randomMany(3) → Returns a new array containing three unique random pies from the array
<Array>.shuffle() → arrayRandomly shuffles the array.
v2.0.0// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
$pies.shuffle() → Randomizes the order of the pies in the array
<Array>.unshiftUnique(members…) → numberPrepends one or more unique members to the beginning of the base array and returns its new length.
v2.21.0members: (any) The members to append.// Given: $fruits = ["Oranges", "Plums"]
$fruits.unshiftUnique("Oranges") → Returns 2; $fruits ["Oranges", "Plums"]
$fruits.unshiftUnique("Apples", "Apples") → Returns 3; $fruits ["Apples", "Oranges", "Plums"]
<Array>.contains(needle [, position]) → booleanDeprecated:
This method has been deprecated and should no longer be used. See the <Array>.includes() method for its replacement.
v2.0.0: Basic syntax.v2.10.0: Deprecated in favor of <Array>.includes().
<Array>.containsAll(needles…) → booleanDeprecated:
This method has been deprecated and should no longer be used. See the <Array>.includesAll() method for its replacement.
v2.0.0: Basic syntax.v2.10.0: Deprecated in favor of <Array>.includesAll().
<Array>.containsAny(needles…) → booleanDeprecated:
This method has been deprecated and should no longer be used. See the <Array>.includesAny() method for its replacement.
v2.0.0: Basic syntax.v2.10.0: Deprecated in favor of <Array>.includesAny().
Array.random(array) → anyDeprecated:
This method has been deprecated and should no longer be used. In general, look to the <Array>.random() method instead. If you need a random member from an array-like object, use the Array.from() method to convert it to an array, then use <Array>.random().
Returns a random member from the array or array-like object. Does not modify the original.
v2.0.0: Basic syntax.v2.20.0: Deprecated.array: (array) The array to operate on. May be an actual array or an array-like object.// Given: $pies = ["Blueberry", "Cherry", "Cream", "Pecan", "Pumpkin"]
Array.random($pies) → Returns a random pie from the array
JSON.reviveWrapper(codeString [, reviveData]) → arrayReturns the given code string, and optional data chunk, wrapped within the JSON deserialization revive wrapper. Intended to allow authors to easily wrap their custom object types (a.k.a. classes) revival code and associated data within the revive wrapper, which should be returned from an object instance's .toJSON() method, so that the instance may be properly revived upon deserialization.
SEE: Non-generic object types (a.k.a. classes) for more detailed information.
v2.0.0: Basic syntax.v2.9.0: Added reviveData parameter.codeString: (string) The revival code string to wrap.reviveData: (optional, any) The data which should be made available to the evaluated revival code during deserialization via the special $ReviveData$ variable. WARNING: Attempting to pass the value of an object instance's this directly as the reviveData parameter will trigger out of control recursion in the serializer, so a clone of the instance's own data must be passed instead.JSON.reviveWrapper( /* valid JavaScript code string */ ); → Without data chunk
JSON.reviveWrapper( /* valid JavaScript code string */ , myOwnData); → With data chunk
// E.g. Assume that you're attempting to revive an instance of a custom class named
// `Character`, which is assigned to a story variable named `$pc`. The call
// to `JSON.reviveWrapper()` might look something like the following.
var ownData = {};
Object.keys(this).forEach(function (pn) { ownData[pn] = clone(this[pn]); }, this);
return JSON.reviveWrapper('new Character($ReviveData$)', ownData);
<jQuery>.ariaClick([options ,] handler) → jQuery objectMakes the target element(s) WAI-ARIA-compatible clickables—meaning that various accessibility attributes are set and, in addition to mouse clicks, enter/return and spacebar key presses also activate them. Returns a reference to the current jQuery object for chaining.
v2.0.0options: (optional, object) The options to be used when creating the clickables.handler: (function) The callback to invoke when the target element(s) are activated.An options object should have some of the following properties:
namespace: (string) A period-separated list of event namespaces.one: (boolean) Whether the clickables are single-use—i.e. the handler callback runs only once and then removes itself. If omitted, defaults to false.selector: (string) A selector applied to the target element(s) to filter the descendants which triggered the event. If omitted or null, the event is always handled when it reaches the target element(s).data: (any) Data to be passed to the handler in event.data when an event is triggered.controls: (string) Value for the aria-controls attribute.pressed: (string) Value for the aria-pressed attribute (valid values: "true", "false").label: (string) Value for the aria-label and title attributes.// Given an existing element: <a id="so-clicky">Click me</a>
$('#so-clicky').ariaClick(function (event) {
/* do stuff */
});
// Creates a basic link and appends it to the `output` element
$('<a>Click me</a>')
.ariaClick(function (event) {
/* do stuff */
})
.appendTo(output);
// Creates a basic button and appends it to the `output` element
$('<button>Click me</button>')
.ariaClick(function (event) {
/* do stuff */
})
.appendTo(output);
// Creates a link with options and appends it to the `output` element
$('<a>Click me</a>')
.ariaClick({
one : true,
label : 'This single-use link does stuff.'
}, function (event) {
/* do stuff */
})
.appendTo(output);
<jQuery>.ariaDisabled(state) → jQuery objectChanges the disabled state of the target WAI-ARIA-compatible clickable element(s). Returns a reference to the current jQuery object for chaining.
NOTE: This method is meant to work with clickables created via <jQuery>.ariaClick() and may not work with clickables from other sources. SugarCube uses <jQuery>.ariaClick() internally to handle all its various link markup and macros.
v2.26.0state: (boolean) The disabled state to apply. Truthy to disable the element(s), falsy to enable them.// Given an existing WAI-ARIA-compatible clickable element with the ID "so-clicky"
$('#so-clicky').ariaDisabled(true) → Disables the target element
$('#so-clicky').ariaDisabled(false) → Enables the target element
<jQuery>.ariaIsDisabled() → booleanReturns whether any of the target WAI-ARIA-compatible clickable element(s) are disabled.
NOTE: This method is meant to work with clickables created via <jQuery>.ariaClick() and may not work with clickables from other sources. SugarCube uses <jQuery>.ariaClick() internally to handle all its various link markup and macros.
v2.26.0// Given an existing WAI-ARIA-compatible clickable element with the ID "so-clicky"
// If "#so-clicky" is disabled:
$('#so-clicky').ariaIsDisabled() → Returns true
// If "#so-clicky" is enabled:
$('#so-clicky').ariaIsDisabled() → Returns false
jQuery.wiki(sources…)Wikifies the given content source(s) and discards the result. If there were errors, an exception is thrown. This is only really useful when you want to invoke a macro for its side-effects and aren't interested in its output.
v2.17.0sources: (string) The list of content sources.$.wiki('<<somemacro>>'); → Invokes the <<somemacro>> macro, discarding any output
<jQuery>.wiki(sources…) → jQuery objectWikifies the given content source(s) and appends the result to the target element(s). Returns a reference to the current jQuery object for chaining.
v2.0.0sources: (string) The list of content sources.// Given an element: <div id="the-box"></div>
$('#the-box').wiki('Who //are// you?'); → Appends "Who <em>are</em> you?" to the target element
Math.clamp(num , min , max) → numberReturns the given number clamped to the specified bounds. Does not modify the original.
v2.0.0num: (number | string) The number to clamp. May be an actual number or a numerical string.min: (integer) The lower bound of the number.max: (integer) The upper bound of the number.Math.clamp($stat, 0, 200) → Clamps $stat to the bounds 0–200 and returns the new value
Math.clamp($stat, 1, 6.6) → Clamps $stat to the bounds 1–6.6 and returns the new value
Math.trunc(num) → integerReturns the whole (integer) part of the given number by removing its fractional part, if any. Does not modify the original.
v2.0.0num: (number) The number to truncate to an integer.Math.trunc(12.7) → Returns 12
Math.trunc(-12.7) → Returns -12
<Number>.clamp(min , max) → numberReturns the number clamped to the specified bounds. Does not modify the original.
v2.0.0min: (integer) The lower bound of the number.max: (integer) The upper bound of the number.$stat.clamp(0, 200) → Clamps $stat to the bounds 0–200 and returns the new value
$stat.clamp(1, 6.6) → Clamps $stat to the bounds 1–6.6 and returns the new value
RegExp.escape(text) → stringReturns the given string with all regular expression metacharacters escaped. Does not modify the original.
v2.0.0text: (string) The string to escape.RegExp.escape('That will be $5 (cash only)') → Returns 'That will be \$5 \(cash only\)'
NOTE: Strings in TwineScript/JavaScript are Unicode, however, due to historic reasons they are comprised of, and indexed by, individual UTF-16 code units rather than code points. This means that some code points may span multiple code units—e.g. the character 💩 is one code point, but two code units.
<String>.count(needle [, position]) → integerReturns the number of times that the given substring was found within the string, starting the search at position.
v2.0.0needle: (any) The substring to count.position: (optional, integer) The zero-based index at which to begin searching for needle. If omitted, will default to 0.// Given: $text = "How now, brown cow."
$text.count("ow") → Returns 4
$text.count("ow", 8) → Returns 2
<String>.first() → stringReturns the first Unicode code point within the string. Does not modify the original.
SEE: String Methods note.
v2.27.0// Given: $text = "abc"
$text.first() → Returns "a"
// Given: $text = "🙈🙉🙊"
$text.first() → Returns "🙈"
String.format(format , arguments…) → stringReturns a formatted string, after replacing each format item in the given format string with the text equivalent of the corresponding argument's value.
v2.0.0format: (string) The format string, which consists of normal text and format items.
{index[,alignment]}, square-brackets denoting optional elements.
index: (integer) The (zero-based) index of the argument whose string representation will replace the format item.alignment: (optional, integer) The total length of the field into which the argument is inserted, and whether it's right- or left-aligned (positive aligns right, negative aligns left).arguments: (any | array) Either a list of arguments, which correspond by-index to the format items within the format string, or an array, whose members correspond by-index.String.format("{0}, {1}!", "Hello", "World") → List of arguments; Returns "Hello, World!"
String.format("{0}, {1}!", [ "Hello", "World" ]) → Array argument; Returns "Hello, World!"
String.format("{0,6}", "foo") → Returns " foo"
String.format("{0,-6}", "foo") → Returns "foo "
<String>.includes(needle [, position]) → booleanReturns whether the given substring was found within the string, starting the search at position.
needle: (any) The substring to find.position: (optional, integer) The zero-based index at which to begin searching for needle. If omitted, will default to 0.// Given: $text = "How now, brown cow."
$text.includes("row") → Returns true
$text.includes("row", 14) → Returns false
$text.includes("cow", 14) → Returns true
$text.includes("pow") → Returns false
<String>.last() → stringReturns the last Unicode code point within the string. Does not modify the original.
SEE: String Methods note.
v2.27.0// Given: $text = "abc"
$text.last() → Returns "c"
// Given: $text = "🙈🙉🙊"
$text.last() → Returns "🙊"
<String>.toLocaleUpperFirst() → stringReturns the string with its first Unicode code point converted to upper case, according to any locale-specific rules. Does not modify the original.
SEE: String Methods note.
v2.9.0// Using the Turkish (Türkçe) locale and given: $text = "ışık"
$text.toLocaleUpperFirst() → Returns "Işık"
// Using the Turkish (Türkçe) locale and given: $text = "iki"
$text.toLocaleUpperFirst() → Returns "İki"
<String>.toUpperFirst() → stringReturns the string with its first Unicode code point converted to upper case. Does not modify the original.
SEE: String Methods note.
v2.9.0// Given: $text = "hello."
$text.toUpperFirst() → Returns "Hello."
// Given: $text = "χαίρετε."
$text.toUpperFirst() → Returns "Χαίρετε."
Passage, tag, and variable names which have special meaning to SugarCube.
PassageDoneUsed for post-passage-display tasks, like redoing dynamic changes (happens after the rendering and display of each passage). Roughly equivalent to the :passagedisplay event.
v2.0.0
PassageFooterAppended to each rendered passage. Roughly equivalent to the :passagerender event.
v2.0.0
PassageHeaderPrepended to each rendered passage. Roughly equivalent to the :passagestart event.
v2.0.0
PassageReadyUsed for pre-passage-display tasks, like redoing dynamic changes (happens before the rendering of each passage). Roughly equivalent to the :passagestart event.
v2.0.0
StartTwine 2: Not special. Any passage may be chosen as the starting passage by selecting it via the Start Story Here passage context-menu item—n.b. older versions of Twine 2 used a (rocket ship icon) for the same purpose.
Twine 1/Twee: Required. The starting passage, the first passage displayed. Configurable, see Config.passages.start for more information.
v2.0.0
StoryAuthorSets the authorial byline in the UI bar (element ID: story-author).
v2.0.0
StoryBannerSets the story's banner in the UI bar (element ID: story-banner).
v2.0.0
StoryCaptionSets the story's caption in the UI bar (element ID: story-caption).
v2.0.0
StoryInitUsed for pre-story-start initialization tasks, like variable initialization (happens at the beginning of story initialization).
v2.0.0
StoryInterfaceUsed to replace SugarCube's default UI. Its contents are treated as raw HTML markup—i.e. none of SugarCube's special HTML processing is performed. It must contain, at least, an element with the ID passages, which will be the main passage display area. Elements, aside from the #passages element, may include a data-passage content attribute, which denotes that the element should be updated via the specified passage—the passage will be processed as normal, meaning that markup and macros will work as expected.
NOTE: Elements which include a data-passage content attribute must not themselves contain additional elements—since such elements' contents are replaced each turn via their associated passage, any child elements would be lost.
v2.18.0: Basic syntax.v2.28.0: Added processing of the data-passage content attribute.<div id="passages"></div>
data-passage content attributes<div id="interface">
<div id="menu" data-passage="Menu"></div>
<div id="notifications" data-passage="Notifications"></div>
<div id="passages"></div>
</div>
StoryMenuSets the story's menu items in the UI bar (element ID: menu-story). NOTE: The story menu only displays links. While it renders content just as any other passage does, instead of displaying the rendered output as-is, it sifts through the output and builds its menu from the generated links contained therein.
v2.0.0
StorySettingsUnused by SugarCube. The Configuration API serves the same basic purpose.
StoryShareUsed to populate the contents of the Share dialog.
v2.0.0
StorySubtitleSets the story's subtitle in the UI bar (element ID: story-subtitle).
v2.0.0
StoryTitleTwine 2: Unused. The story's title/name is part of the story project.
Twine 1/Twee: Required. Sets the story's title in the UI bar and elsewhere (element ID: story-title). NOTE: The story title should the project's plain text title and contain no markup.
v2.0.0
bookmarkRegisters the passage into the Jump To menu.
v2.0.0
nobrCauses leading/trailing newlines to be removed and all remaining sequences of newlines to be replaced with single spaces before the passage is rendered. Equivalent to wrapping the entire passage in a <<nobr>> macro. See the Config.passages.nobr setting for a way to apply the same processing to all passages at once.
v2.0.0
scriptTwine 2: Not special. Use the Edit Story JavaScript story editor menu item for scripts.
Twine 1/Twee: Registers the passage as JavaScript code, which is executed during startup.
v2.0.0
stylesheetTwine 2: Not special. Use the Edit Story Stylesheet story editor menu item for styles.
Twine 1/Twee: Registers the passage as a CSS stylesheet, which is loaded during startup. It is strongly recommended that you use only one stylesheet passage. Additionally, see the tagged stylesheet warning.
v2.0.0
Twine.audioRegisters the passage as an audio passage. See Guide: Media Passages for more information.
v2.24.0
Twine.imageRegisters the passage as an image passage. See Guide: Media Passages for more information.
v2.0.0
Twine.videoRegisters the passage as a video passage. See Guide: Media Passages for more information.
v2.24.0
Twine.vttRegisters the passage as a VTT passage. See Guide: Media Passages for more information.
v2.24.0
widgetRegisters the passage as <<widget>> macro definitions, which are loaded during startup.
v2.0.0
$Alias for jQuery, by default. NOTE: This should not be confused with story variables, which start with a $—e.g. $foo.
v2.0.0
$argsWidget arguments array (only inside widgets). See <<widget>> for more information.
v2.0.0
ConfigConfiguration API. See Config API for more information.
v2.0.0
DialogDialog API. See Dialog API for more information.
v2.0.0
EngineEngine API. See Engine API for more information.
v2.0.0
jQueryjQuery library function.
v2.0.0
l10nStringsStrings localization object. See Localization for more information.
v2.10.0
LoadScreenLoadScreen API. See LoadScreen API for more information.
v2.15.0
MacroMacro API. See Macro API for more information.
v2.0.0
PassagePassage API. See Passage API for more information.
v2.0.0
postdisplayPost-display task callback object, set up by the author/developer. See navigation tasks for more information.
v2.0.0
postrenderPost-render task callback object, set up by the author/developer. See navigation tasks for more information.
v2.0.0
predisplayPre-display task callback object, set up by the author/developer. See navigation tasks for more information.
v2.0.0
prehistoryPre-history task callback object, set up by the author/developer. See navigation tasks for more information.
v2.0.0
prerenderPre-render task callback object, set up by the author/developer. See navigation tasks for more information.
v2.0.0
SaveSave API. See Save API for more information.
v2.0.0
SettingSetting API. See Setting API for more information.
v2.0.0
settingsPlayer settings object, set up by the author/developer. See Setting API for more information.
v2.0.0
setupObject that authors/developers may use to set up various bits of static data. Generally, you would use this for data that does not change and should not be stored within story variables, which would make it part of the history.
v2.0.0
StateState API. See State API for more information.
v2.0.0
StoryStory API. See Story API for more information.
v2.0.0
UIUI API. See UI API for more information.
v2.0.0
UIBarUIBar API. See UIBar API for more information.
v2.17.0
IDs and classes automatically generated from passage names and tags are normalized to kebab case with all lowercase letters (which entails: removing characters which are not alphanumerics, underscores, hyphens, en-/em-dashes, or whitespace, then replacing any remaining non-alphanumeric characters with hyphens, one per group, and finally converting the result to lowercase).
Passage names have passage- prepended to their converted forms and are converted both into IDs and classes depending on how the passage is used—an ID for the active passage, classes for included (via <<include>>) passages.
For example, if the passage name was Gone fishin', then:
passage-gone-fishin (selector: #passage-gone-fishin).passage-gone-fishin (selector: .passage-gone-fishin).When displaying a passage, its tags are:
<html> element, and <body> element as a space separated list within the data-tags attribute.<body> element as classes. The following special tags are excluded from this mapping:
| Twine 2: | debug, nobr, passage, widget, and any tag starting with twine. |
|---|---|
| Twine 1/Twee: | debug, nobr, passage, script, stylesheet, widget, and any tag starting with twine. |
For example, if the tag name was Sector_42, then it would become both the data-tags attribute member Sector_42 (selector: [data-tags~="Sector_42"]) and the class sector-42 (selector: .sector-42).
| Selector | Description |
|---|---|
html |
The document element. The default font stack is set here. The active passage's tags will be added to its |
body |
The body of the page. The default foreground and background colors are set here. The active passage's tags will be added to its |
#story |
Selects the story element. |
#passages |
Selects the element which contains passage elements. All created passage elements will be children of this element. |
.passage |
Selects the passage element. Normally, there will be only one such passage per turn, however, during passage navigation there may briefly be two—the incoming (a.k.a. active) and outgoing passages. The active passage's name will be added as its ID (see: Passage Conversions). The active passage's tags will be added to its |
.passage a |
Selects all <a> elements within the passage element. |
.passage a:hover |
Selects <a> elements within the passage element which are being hovered over. |
.passage a:active |
Selects <a> elements within the passage element which are being clicked on. |
.passage .link-broken |
Selects all internal link elements within the passage element whose passages do not exist within the story. |
.passage .link-disabled |
Selects all internal link elements within the passage element who have been disabled (e.g. already chosen <<choice>> macro links). |
.passage .link-external |
Selects all external link elements within the passage element (e.g. links to other pages and websites). |
.passage .link-internal |
Selects all internal link elements within the passage element (e.g. passage and macro links). |
.passage .link-visited1 |
Selects all internal link elements within the passage element whose passages are within the in-play story history (i.e. passages the player has been to before). |
.passage .link-internal:not(.link-visited)1 |
Selects all internal link elements within the passage element whose passages are not within the in-play story history (i.e. passages the player has never been to before). |
.link-visited class is not enabled by default, see the Config API's Config.addVisitedLinkClass property for more information.
When using Twine 1/Twee, it is strongly recommended that you use only a single stylesheet tagged passage. CSS styles cascade in order of load, so if you use multiple stylesheet tagged passages, then it is all too easy for your styles to be loaded in the wrong order, since Twine 1/Twee gives you no control over the order in which multiple stylesheet tagged passages load.
SugarCube does not support the Twine 1.4+ vanilla story formats' tagged stylesheets. In SugarCube, you would instead simply prefix the selectors of your styles with the appropriate tag-based selectors—e.g. either [data-tags~="…"] attribute selectors or class selectors.
For example, if some story passages were tagged with forest, then styles for those forest passages might look like this:
/* Using [data-tags~="…"] attribute selectors on <html> */
html[data-tags~="forest"] { background-image: url(forest-bg.jpg); }
html[data-tags~="forest"] .passage { color: darkgreen; }
html[data-tags~="forest"] a { color: green; }
html[data-tags~="forest"] a:hover { color: lime; }
/* Using [data-tags~="…"] attribute selectors on <body> */
body[data-tags~="forest"] { background-image: url(forest-bg.jpg); }
body[data-tags~="forest"] .passage { color: darkgreen; }
body[data-tags~="forest"] a { color: green; }
body[data-tags~="forest"] a:hover { color: lime; }
/* Using class selectors on <body> */
body.forest { background-image: url(forest-bg.jpg); }
body.forest .passage { color: darkgreen; }
body.forest a { color: green; }
body.forest a:hover { color: lime; }
These are SugarCube's built-in stylesheets, in order of load/cascade. The most interesting of which, from an end-user's standpoint, are 4–10. The links go to the most recent release versions of each in SugarCube's public source code repository.
The hierarchy of the document body, including associated HTML IDs and class names (n.b. periods of ellipsis signify data which is dynamically generated at run time) is as follows:
<body class="…">
<div id="init-screen"></div>
<div id="ui-overlay" class="ui-close"></div>
<div id="ui-dialog" tabindex="0" role="dialog" aria-labelledby="ui-dialog-title">
<div id="ui-dialog-titlebar">
<h1 id="ui-dialog-title"></h1>
<button id="ui-dialog-close" class="ui-close" tabindex="0" aria-label="…"></button>
</div>
<div id="ui-dialog-body"></div>
</div>
<div id="ui-bar">
<div id="ui-bar-tray">
<button id="ui-bar-toggle" tabindex="0" title="…" aria-label="…"></button>
<div id="ui-bar-history">
<button id="history-backward" tabindex="0" title="…" aria-label="…">…</button>
<button id="history-jumpto" tabindex="0" title="…" aria-label="…">…</button>
<button id="history-forward" tabindex="0" title="…" aria-label="…">…</button>
</div>
</div>
<div id="ui-bar-body">
<header id="title" role="banner">
<div id="story-banner"></div>
<h1 id="story-title"></h1>
<div id="story-subtitle"></div>
<div id="story-title-separator"></div> <!-- Unused -->
<p id="story-author"></p>
</header>
<div id="story-caption"></div>
<nav id="menu" role="navigation">
<ul id="menu-story">…<ul> <!-- Exists only if used -->
<ul id="menu-core">
<li id="menu-item-saves"><a tabindex="0">…</a></li>
<li id="menu-item-settings"><a tabindex="0">…</a></li> <!-- Exists only if used -->
<li id="menu-item-restart"><a tabindex="0">…</a></li>
<li id="menu-item-share"><a tabindex="0">…</a></li> <!-- Exists only if used -->
</ul>
</nav>
</div>
</div>
<div id="story" role="main">
<div id="passages">
<div class="passage …" id="…" data-passage="…">
<!-- The active (present) passage content -->
</div>
</div>
</div>
<div id="store-area" data-size="…" hidden></div>
<script id="script-sugarcube" type="text/javascript"><!-- The main SugarCube module --></script>
</body>
#story-title-separator element is normally unused.#menu-story, will only exist if used (i.e. no StoryMenu special passage, then no story menu).#menu-item-settings and #menu-item-share, will only exist if they can be used (i.e. if the Setting object is not used, then no Settings menu; if the StoryShare special passage does not exist, then no Share menu).
Navigation events and tasks allow the execution of JavaScript code at specific points during passage navigation.
In order of processing: (for reference, this also shows the Passage… and UI bar special passages)
:passageinit event.prehistory tasks.predisplay tasks.PassageReady special passage.:passagestart event.prerender tasks.PassageHeader special passage.PassageFooter special passage.:passagerender event.postrender tasks.PassageDone special passage.:passagedisplay event.postdisplay tasks.StoryCaption. Happens before the end of passage navigation.:passageend event.
Navigation events are global synthetic events which are triggered at specific points during passage navigation.
:passageinit eventTriggered before the modification of the state history.
v2.20.0passage: (Passage object) The incoming passage object. See the Passage API for more information./* Execute the handler function each time the event triggers. */
$(document).on(':passageinit', function (ev) {
/* JavaScript code */
});
/* Execute the handler function exactly once. */
$(document).one(':passageinit', function (ev) {
/* JavaScript code */
});
:passagestart eventTriggered before the rendering of the incoming passage.
v2.20.0content: (HTMLElement object) The, currently, empty element which will eventually hold the rendered content of the incoming passage.passage: (Passage object) The incoming passage object. See the Passage API for more information./* Execute the handler function each time the event triggers. */
$(document).on(':passagestart', function (ev) {
/* JavaScript code */
});
/* Execute the handler function exactly once. */
$(document).one(':passagestart', function (ev) {
/* JavaScript code */
});
Modifying the content buffer:
/*
Process the markup "Begin at the //beginning//." and append the result
to the incoming passage's element.
*/
$(document).on(':passagestart', function (ev) {
$(ev.content).wiki("Begin at the //beginning//.");
});
:passagerender eventTriggered after the rendering of the incoming passage.
v2.20.0content: (HTMLElement object) The element holding the fully rendered content of the incoming passage.passage: (Passage object) The incoming passage object. See the Passage API for more information./* Execute the handler function each time the event triggers. */
$(document).on(':passagerender', function (ev) {
/* JavaScript code */
});
/* Execute the handler function exactly once. */
$(document).one(':passagerender', function (ev) {
/* JavaScript code */
});
Modifying the content buffer:
/*
Process the markup "At the //end// of all things." and append the result
to the incoming passage's element.
*/
$(document).on(':passagerender', function (ev) {
$(ev.content).wiki("At the //end// of all things.");
});
:passagedisplay eventTriggered after the display (i.e. output) of the incoming passage.
v2.20.0passage: (Passage object) The incoming passage object. See the Passage API for more information./* Execute the handler function each time the event triggers. */
$(document).on(':passagedisplay', function (ev) {
/* JavaScript code */
});
/* Execute the handler function exactly once. */
$(document).one(':passagedisplay', function (ev) {
/* JavaScript code */
});
:passageend eventTriggered at the end of passage navigation.
v2.20.0passage: (Passage object) The incoming passage object. See the Passage API for more information./* Execute the handler function each time the event triggers. */
$(document).on(':passageend', function (ev) {
/* JavaScript code */
});
/* Execute the handler function exactly once. */
$(document).one(':passageend', function (ev) {
/* JavaScript code */
});
Navigation tasks are functions which are called at specific points during passage navigation.
NOTE: Tasks are an older method of enabling passage navigation events and you are encouraged to use navigation events instead as they allow for easier control and are triggered at better points during navigation.
prehistory task functionsExecuted before the modification of the state history.
v2.0.0taskName: (string) The name of the executing task.prehistory["Some Task Name"] = function (taskName) {
/* JavaScript code */
};
predisplay task functionsExecuted before the rendering of the incoming passage.
v2.0.0taskName: (string) The name of the executing task.predisplay["Some Task Name"] = function (taskName) {
/* JavaScript code */
};
prerender task functionsExecuted before the rendering of the incoming passage.
v2.0.0content: (HTMLElement object) The, likely, empty element which will eventually hold the rendered content of the incoming passage.taskName: (string) The name of the executing task.prerender["Some Task Name"] = function (content, taskName) {
/* JavaScript code */
};
postrender task functionsExecuted after the rendering of the incoming passage.
v2.0.0content: (HTMLElement object) The element holding the fully rendered content of the incoming passage.taskName: (string) The name of the executing task.postrender["Some Task Name"] = function (content, taskName) {
/* JavaScript code */
};
postdisplay task functionsExecuted after the display (i.e. output) of the incoming passage.
v2.0.0taskName: (string) The name of the executing task.postdisplay["Some Task Name"] = function (taskName) {
/* JavaScript code */
};
Config APIThe Config object controls various aspects of SugarCube's behavior.
NOTE: Config object settings should be placed within a script section (Twine 2: the Story JavaScript; Twine 1/Twee: a script-tagged passage).
Config.audio.pauseOnFadeToZero ↔ boolean (default: true)Determines whether the audio subsystem automatically pauses tracks which have been faded to 0 volume (silent).
v2.28.0Config.audio.pauseOnFadeToZero = false;
Config.audio.preloadMetadata ↔ boolean (default: true)Determines whether the audio subsystem attempts to preload track metadata—meaning information about the track (e.g. duration), not its audio frames.
NOTE: It's unlikely that you'll ever want to disable this setting.
v2.28.0Config.audio.preloadMetadata = false;
Config.history.controls ↔ boolean (default: true)Determines whether the story's history controls (Backward, Jump To, & Forward buttons) are enabled within the UI bar.
v2.0.0Config.history.controls = false;
Config.history.maxStates ↔ integer (default: 100)Sets the maximum number of states (moments) to which the history is allowed to grow. Should the history exceed the limit, states will be dropped from the past (oldest first). A setting of 0 means that there is no limit to how large the history may grow, though doing so is not recommended.
v2.0.0// No history limit (you should never do this!)
Config.history.maxStates = 0;
// Limit the history to a single state
Config.history.maxStates = 1;
// Limit the history to 150 states
Config.history.maxStates = 150;
Config.macros.ifAssignmentError ↔ boolean (default: true)Determines whether the <<if>> macro returns an error when the = assignment operator is used within its conditional. Does not flag other assignment operators.
NOTE: This setting exists because it's unlikely that you'll ever want to actually perform an assignment within a conditional expression and typing = when you meant === (or ==) is a fairly easy to mistake make—either from a finger slip or because you just don't know the difference between the operators.
v2.0.0// No error is returned when = is used
Config.macros.ifAssignmentError = false;
Config.macros.maxLoopIterations ↔ integer (default: 1000)Sets the maximum number of iterations allowed before the <<for>> macro conditional forms are terminated with an error.
NOTE: This setting exists to prevent a misconfigured loop from making the browser unresponsive.
v2.0.0// Allow only 5000 iterations
Config.macros.maxLoopIterations = 5000;
Config.navigation.override ↔ function (default: none)Allows the destination of passage navigation to be overridden. The callback is passed one parameter, the original destination passage title. If its return value is falsy, the override is cancelled and navigation to the original destination continues unperturbed. If its return value is truthy, the override succeeds and that value is used as the new destination of the navigation.
v2.13.0Config.navigation.override = function (destinationPassage) {
/* code */
};
// Force the player to the "You Died" passage if they let $health get too low.
Config.navigation.override = function (dest) {
var sv = State.variables;
// If $health is less-than-or-equal to 0, go to the "You Died" passage instead.
if (sv.health <= 0) {
return "You Died";
}
};
Config.passages.descriptions ↔ boolean | object | function (default: none)Determines whether alternate passage descriptions are used by the Saves and Rewind menus (by default an excerpt from the passage is used). Valid values are boolean true, which simply causes the passages' titles to be used, an object, which maps passages' titles to their descriptions, or a function, which should return the passages' description.
NOTE (as boolean): You should ensure that all encounterable passage titles are meaningful.
NOTE (as object): If the mapping object does not contain an entry for the passage in question, then the passage's excerpt is used instead.
NOTE (as function): The function is called with the passage in question as its this value. If the function returns falsy, then the passage's excerpt is used instead.
v2.0.0// Uses passages' titles
Config.passages.descriptions = true;
// Uses the description mapped by the title
Config.passages.descriptions = {
"title" : "description"
};
// Returns the description to be used
Config.passages.descriptions = function () {
if (this.title === "title") {
return "description";
}
};
Config.passages.displayTitles ↔ boolean (default: false)Determines whether passage titles are combined with the story title, within the browser's/tab's titlebar, when passages are displayed.
v2.0.0Config.passages.displayTitles = true;
Config.passages.nobr ↔ boolean (default: false)Determines whether rendering passages have their leading/trailing newlines removed and all remaining sequences of newlines replaced with single spaces before they're rendered. Equivalent to including the nobr special tag on every passage.
NOTE (Twine 1/Twee): Does not affect script or stylesheet passages.
v2.19.0Config.passages.nobr = true;
Config.passages.start ↔ string (Twine 2 default: none; Twine 1/Twee default: "Start")Sets the title of the initial passage, the very first passage which will be displayed.
v2.0.0Config.passages.start = "That Other Starting Passage";
Config.passages.transitionOut ↔ string | integer (default: none)Determines whether outgoing passage transitions are enabled. Valid values are the name of the property being animated, which causes the outgoing passage elements to be removed once that transition animation is complete, or an integer delay (in milliseconds), which causes the outgoing passage elements to be removed once the delay has expired.
NOTE: If using an integer delay, ideally, it should probably be slightly longer than the outgoing transition delay which you intend to use (10ms or so should be sufficient).
v2.0.0// Remove outgoing elements when their opacity animation ends
Config.passages.transitionOut = "opacity";
// Remove outgoing elements after 1010ms (1.01s)
Config.passages.transitionOut = 1010;
Config.saves.autoload ↔ boolean | string | function (default: none)Determines whether the autosave, if it exists, is automatically loaded upon story startup. Valid values are boolean true, which simply causes the autosave to be loaded, the string "prompt", which prompts the player via a dialog to load the autosave, or a function, which causes the autosave to be loaded if its return value is truthy.
NOTE: If the autosave cannot be loaded, for any reason, then the start passage is loaded instead.
v2.0.0// Automatically loads the autosave
Config.saves.autoload = true;
// Prompts the player about loading the autosave
Config.saves.autoload = "prompt";
// Loads the autosave if it returns a truthy value
Config.saves.autoload = function () {
/* code */
};
Config.saves.autosave ↔ boolean | string | string array (default: none)Determines whether the autosave is created/updated when passages are displayed. Valid values are boolean true, which causes the autosave to be updated with every passage, a string, which causes the autosave to be updated for each passage with a matching tag, or an array of strings, which causes the autosave to be updated for each passage with at least one matching tag.
NOTE: When setting the value to boolean true, you will likely also need to use the Config.saves.isAllowed property to disallow saving on the start passage. Or, if you use the start passage as real part of your story and allow the player to reenter it, rather than just as the initial landing/cover page, then you might wish to only disallow saving on the start passage the very first time it's displayed (at story startup).
v2.0.0// Autosaves every passage
Config.saves.autosave = true;
// Autosaves on passages tagged with "thunderbirds"
Config.saves.autosave = "thunderbirds";
// Autosaves on passages tagged with any of "bookmark" or "autosave"
Config.saves.autosave = ["bookmark", "autosave"];
Config.saves.id ↔ string (default: slugified story title)Sets the story ID associated with saves.
v2.0.0Config.saves.id = "a-big-huge-story-part-1";
Config.saves.isAllowed ↔ function (default: none)Determines whether saving is allowed within the current context. The callback is invoked each time a save is requested. If its return value is falsy, the save is disallowed. If its return value is truthy, the save is allowed to continue unperturbed.
v2.0.0Config.saves.isAllowed = function () {
/* code */
};
Config.saves.onLoad ↔ function (default: none)Performs any required pre-processing before the save data is loaded. The callback is passed one parameter, the save object to be processed. If it encounters an unrecoverable problem during its processing, it may throw an exception containing an error message; the message will be displayed to the player and loading of the save will be terminated.
NOTE: See the save objects section of the Save API for information on the format of a save.
v2.0.0Config.saves.onLoad = function (save) {
/* code */
};
Config.saves.onSave ↔ function (default: none)Performs any required post-processing before the save data is saved. The callback is passed one parameter, the save object to be processed.
NOTE: See the save objects section of the Save API for information on the format of a save.
v2.0.0Config.saves.onSave = function (save) {
/* code */
};
Config.saves.slots integer (default: 8)Sets the maximum number of available save slots.
v2.0.0Config.saves.slots = 4;
Config.saves.version ↔ any (default: none)Sets the version property of saves.
NOTE: This completely optional property is only for developer use, it is ignored by SugarCube.
v2.0.0// As an integer
Config.saves.version = 3;
// As a string
Config.saves.version = "v3";
Config.ui.stowBarInitially ↔ boolean | integer (default: 800)Determines whether the UI bar (sidebar) starts in the stowed (shut) state initially. Valid values are boolean true/false, which causes the UI bar to always/never start in the stowed state, or an integer, which causes the UI bar to start in the stowed state if the viewport width is less-than-or-equal-to the specified number of pixels.
v2.11.0// As a boolean; always start stowed
Config.ui.stowBarInitially = true;
// As a boolean; never start stowed
Config.ui.stowBarInitially = false;
// As an integer; start stowed if the viewport is 800px or less
Config.ui.stowBarInitially = 800;
Config.ui.updateStoryElements ↔ boolean (default: true)Determines whether certain elements within the UI bar are updated when passages are displayed. The affected elements are the story: banner, subtitle, author, caption, and menu.
NOTE: SugarCube uses the story's title as the basis for the key used to store and load data used when playing the story and for saves. Because of this, the story title is not included in updates and it is strongly recommended that you do not add any kind of dynamic code to it.
v2.0.0// If you don't need those elements to update
Config.ui.updateStoryElements = false;
Config.addVisitedLinkClass ↔ boolean (default: false)Determines whether the link-visited class is added to internal passage links which go to previously visited passages—i.e. the passage already exists within the story history.
NOTE: You must provide your own styling for the link-visited class as none is provided by default.
v2.0.0Config.addVisitedLinkClass = true;
An example style: (Twine 2: goes in Story Stylesheet; Twine 1/Twee: goes in a stylesheet-tagged passage)
.link-visited {
color: purple;
}
Config.cleanupWikifierOutput ↔ boolean (default: false)Determines whether the output of the Wikifier is post-processed into more sane markup—i.e. where appropriate, it tries to transition the plethora of <br> elements into <p> elements.
v2.0.0Config.cleanupWikifierOutput = true;
Config.debug ↔ boolean (default: false)Indicates whether SugarCube is running in test mode, which enables debug views. See Test Mode for more information.
NOTE: This property is automatically set based on whether you're using a testing mode in a Twine compiler—i.e. Test mode in Twine 2, Test Play From Here in Twine 1, or the test mode options (-t, --test) in Tweego. You may, however, forcibly enable it if you need to for some reason—e.g. if you're using another compiler, which doesn't offer a way to enable test mode.
v2.2.0// Forcibly enable test mode
Config.debug = true;
// Check if test mode is enabled in JavaScript
if (Config.debug) {
/* do something debug related */
}
// Check if test mode is enabled via the <<if>> macro
<<if Config.debug>>
/* do something debug related */
<</if>>
Config.loadDelay ↔ integer (default: 0)Sets the integer delay (in milliseconds) before the loading screen is dismissed, once the document has signaled its readiness. Not generally necessary, however, some browsers render slower than others and may need a little extra time to get a media-heavy page done. This allows you to fine tune for those cases.
v2.0.0// Delay the dismissal of the loading screen by 2000ms (2s)
Config.loadDelay = 2000;
Dialog API
Dialog.addClickHandler(targets [, options [, startFn [, doneFn [, closeFn]]]])Adds WAI-ARIA-compatible mouse/keyboard event handlers to the target element(s) which open the dialog when activated.
v2.0.0target: (HTMLElement object | jQuery object | string) The DOM element(s) to attach the handler to—may be either an HTMLElement object, a jQuery object, or a jQuery-style selector set.options: (optional, null | object) The options object; the currently understood properties are:
top: Top y-coordinate of the dialog (default: 50; in pixels, but without the unit).opacity: Opacity of the overlay (default: 0.8).startFn: (optional, null | function) The function to execute at the start of Dialog.addClickHandler(). This is commonly used to setup the dialog.doneFn: (optional, null | function) The function to execute at the end of Dialog.addClickHandler().closeFn: (optional, null | function) The function to execute whenever the associated dialog is closed.// Commonly used something like the following.
Dialog.addClickHandler("#some-element", null, function () {
Dialog.setup("My Dialog Title", "my-dialog-class");
Dialog.wiki(Story.get("MyDialogContents").processText());
});
Dialog.append(content) → Dialog objectAppends the given content to the dialog's content area. Returns a reference to the Dialog object for chaining.
NOTE: If your content contains any SugarCube markup, you'll need to use the Dialog.wiki() method instead.
v2.9.0content: The content to append. As this method is essentially a shortcut for jQuery(Dialog.body()).append(…), see jQuery's append() method for the range of valid content types.Dialog.append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");
Dialog.append( /* some DOM nodes */ );
Dialog.body() → HTMLElement objectReturns a reference to the dialog's content area.
v2.0.0jQuery(Dialog.body()).append("Cry 'Havoc!', and let slip the <em>ponies</em> of <strong>friendship</strong>.");
jQuery(Dialog.body()).wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
Dialog.close() → Dialog objectCloses the dialog. Returns a reference to the Dialog object for chaining.
v2.0.0Dialog.close();
Dialog.isOpen([classNames]) → booleanReturns whether the dialog is currently open.
v2.0.0classNames: (optional, string) The space-separated-list of classes to check for when determining the state of the dialog. Each of built-in dialogs contains a name-themed class which can be tested for in this manner—e.g. the Saves dialog contains the class saves.if (Dialog.isOpen()) {
/* code to execute if the dialog is open… */
}
if (Dialog.isOpen("saves")) {
/* code to execute if the Saves dialog is open… */
}
Dialog.open([options [, closeFn]]) → Dialog objectOpens the dialog. Returns a reference to the Dialog object for chaining.
NOTE: Call this only after populating the dialog with content.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.Dialog.open();
Dialog.setup([title [, classNames]]) → HTMLElement objectPrepares the dialog for use and returns a reference to its content area.
v2.0.0title: (optional, string) The title of the dialog.classNames: (optional, string) The space-separated-list of classes to add to the dialog.// Basic example.
Dialog.setup();
Dialog.wiki("Blah //blah// ''blah''.");
Dialog.open();
// Adding a title to the dialog.
Dialog.setup("Character Sheet");
Dialog.wiki(Story.get("PC Sheet").processText());
Dialog.open();
// Adding a title and class to the dialog.
Dialog.setup("Character Sheet", "charsheet");
Dialog.wiki(Story.get("PC Sheet").processText());
Dialog.open();
Dialog.wiki(wikiMarkup) → Dialog objectRenders the given markup and appends it to the dialog's content area. Returns a reference to the Dialog object for chaining.
NOTE: If your content consists of DOM nodes, you'll need to use the Dialog.append() method instead.
v2.9.0wikiMarkup: The markup to render.Dialog.wiki("Cry 'Havoc!', and let slip the //ponies// of ''friendship''.");
Engine API
Engine.lastPlay → numberReturns a timestamp representing the last time Engine.play() was called.
v2.0.0Engine.lastPlay → The timestamp at which Engine.play() was last called
Engine.state → stringReturns the current state of the engine ("idle", "playing", "rendering").
v2.7.0"idle": The engine is idle, awaiting the triggering of passage navigation—the default state."playing": Passage navigation has been triggered and a turn is being processed."rendering": The incoming passage is being rendered and added to the page—takes place during turn processing, so implies "playing".Engine.state → Returns the current state of the engine
Engine.backward() → booleanMoves backward one moment within the full history (past + future), if possible, activating and showing the moment moved to. Returns whether the history navigation was successful (should only fail if already at the beginning of the full history).
v2.0.0Engine.backward() → Rewinds the full history by one moment (i.e. undoes the moment)
Engine.forward() → booleanMoves forward one moment within the full history (past + future), if possible, activating and showing the moment moved to. Returns whether the history navigation was successful (should only fail if already at the end of the full history).
v2.0.0Engine.forward() → Fast forwards the full history by one moment (i.e. redoes the moment)
Engine.go(offset) → booleanActivates the moment at the given offset from the active (present) moment within the full state history and show it. Returns whether the history navigation was successful (should only fail if the offset from the active (present) moment is not within the bounds of the full history).
v2.0.0offset: (integer) The offset from the active (present) moment of the moment to go to.Engine.go(2) → Fast forwards the full history by two moments (i.e. redoes the moments)
Engine.go(-4) → Rewinds the full history by four moments (i.e. undoes the moments)
Engine.goTo(index) → booleanActivates the moment at the given index within the full state history and show it. Returns whether the history navigation was successful (should only fail if the index is not within the bounds of the full history).
v2.0.0index: (integer) The index of the moment to go to.Engine.goTo(0) → Goes to the first moment
Engine.goTo(9) → Goes to the tenth moment
Engine.isIdle() → booleanReturns whether the engine is idle.
v2.16.0Engine.isIdle() → Returns whether the engine is idle
Engine.isPlaying() → booleanReturns whether the engine is processing a turn—i.e. passage navigation has been triggered.
v2.16.0Engine.isPlaying() → Returns whether the engine is playing
Engine.isRendering() → booleanReturns whether the engine is rendering the incoming passage.
v2.16.0Engine.isRendering() → Returns whether the engine is rendering
Engine.play(passageTitle [, noHistory]) → HTMLElement objectRenders and displays the passage referenced by the given title, optionally without adding a new moment to the history.
v2.0.0passageTitle: (string) The title of the passage to play.noHistory: (optional, boolean) Disables the update of the history (i.e. no moment is added to the history).Engine.play("Foo") → Renders, displays, and adds a moment for the passage "Foo" to the history
Engine.play("Foo", true) → Renders and displays the passage "Foo", but does not add new history
Engine.restart()Restarts the story.
WARNING: The player will not be prompted and all unsaved state will be lost.
NOTE: In general, you should not call this method directly. Instead, call the UI.restart() static method, which prompts the player with an OK/Cancel dialog before itself calling Engine.restart(), if they accept.
v2.0.0Engine.restart() → Restarts the story
Engine.show() → HTMLElement objectRenders and displays the active (present) moment's associated passage without adding a new moment to the history.
v2.0.0Engine.show() → Renders and displays the present passage without adding new history
:enginerestart eventGlobal synthetic event triggered just before the page is reloaded when Engine.restart() is called. Allows last second bookkeeping tasks.
v2.23.0/* Execute the handler function when the event triggers. */
$(document).on(':enginerestart', function (ev) {
/* JavaScript code */
});
LoadScreen APINOTE: To simply add a delay to the dismissal of the loading screen to hide initial flashes of unstyled content (FOUC)—e.g. style changes and page reflows—you do not need to use this API. See the Config.loadDelay configuration setting.
LoadScreen.lock() → numberAcquires a loading screen lock and returns its ID. Displays the loading screen, if necessary.
v2.15.0LoadScreen.lock() → Locks the loading screen and returns the lock ID
LoadScreen.unlock(lockId)Releases the loading screen lock with the given ID. Hides the loading screen, if no other locks exist.
v2.15.0lockId: (integer) The loading screen lock ID.var lockId = LoadScreen.lock();
// Do something whose timing is unpredictable which should be hidden by the loading screen
LoadScreen.unlock(lockId);
Macro APISibling to the MacroContext API.
Macro.add(name , definition [, deep])Add new macro(s).
v2.0.0name: (string | string array) Name, or array of names, of the macro(s) to add.definition: (object | string) Definition of the macro(s) or the name of an existing macro whose definition to copy.deep: (optional, boolean) Enables deep cloning of the definition. Used to give macros separate instances of the same definition.A macro definition object should have some of the following properties (only handler is absolutely required):
skipArgs: (optional, boolean) Disables parsing argument strings into discrete arguments. Used by macros which only use the raw/full argument strings.tags: (optional, null | string array) Signifies that the macro is a container macro—i.e. not self-closing. An array of the names of the child tags, or null if there are no child tags.handler: (function) The macro's main function. It will be called without arguments, but with its this set to a macro context object.Additional properties may be added for internal use.
/*
Example of a very simple/naive <<if>>/<<elseif>>/<<else>> macro implementation.
*/
Macro.add('if', {
skipArgs : true,
tags : ['elseif', 'else'],
handler : function () {
try {
for (var i = 0, len = this.payload.length; i < len; ++i) {
if (
this.payload[i].name === 'else' ||
!!Scripting.evalJavaScript(this.payload[i].args.full)
) {
jQuery(this.output).wiki(this.payload[i].contents);
break;
}
}
}
catch (ex) {
return this.error('bad conditional expression: ' + ex.message);
}
}
});
Macro.delete(name)Remove existing macro(s).
v2.0.0name: (string | string array) Name, or array of names, of the macro(s) to remove.Macro.delete("amacro")
Macro.delete(["amacro", "bmacro"])
Macro.get(name) → objectReturn the named macro definition, or null on failure.
v2.0.0name: (string) Name of the macro whose definition should be returned.Macro.get("print")
Macro.has(name) → booleanReturns whether the named macro exists.
v2.0.0name: (string) Name of the macro to search for.Macro.has("print")
Macro.tags.get(name) → string arrayReturn the named macro tag's parents array (includes the names of all macros who have registered the tag as a child), or null on failure.
v2.0.0name: (string) Name of the macro tag whose parents array should be returned.Macro.tags.get("else") → For the standard library, returns: ["if"]
Macro.tags.has(name) → booleanReturns whether the named macro tag exists.
v2.0.0name: (string) Name of the macro tag to search for.Macro.tags.has("else")
MacroContext APISibling to the Macro API. Macro handlers are called with no arguments, but with their this set to a macro (execution) context object. Macro context objects contain the following data and method properties.
<MacroContext>.args → arrayThe argument string parsed into an array of discrete arguments.
v2.0.0// Given: <<someMacro "a" "b" "c">>
this.args.length → Returns 3
this.args[0] → Returns 'a'
this.args[1] → Returns 'b'
this.args[2] → Returns 'c'
<MacroContext>.args.full → stringThe argument string after converting all TwineScript syntax elements into their native JavaScript counterparts.
v2.0.0// Given: <<if "a" is "b">>
this.args.full → Returns '"a" === "b"'
<MacroContext>.args.raw → stringThe unprocessed argument string.
v2.0.0// Given: <<if "a" is "b">>
this.args.raw → Returns '"a" is "b"'
<MacroContext>.name → stringThe name of the macro.
v2.0.0// Given: <<someMacro …>>
this.name → Returns 'someMacro'
<MacroContext>.output → HTMLElement objectThe current output element.
v2.0.0
<MacroContext>.parent → null | objectThe (execution) context object of the macro's parent, or null if the macro has no parent.
v2.0.0
<MacroContext>.parser → objectThe parser instance which generated the macro call.
v2.0.0
<MacroContext>.payload → null | arrayThe text of a container macro parsed into discrete payload objects by tag. Payload objects have the following properties:
name: (string) Name of the current tag.args: (array) The current tag's argument string parsed into an array of discrete arguments. Equivalent in function to <MacroContext>.args.
args.full: (string) The current tag's argument string after converting all TwineScript syntax elements into their native JavaScript counterparts. Equivalent in function to <MacroContext>.args.full.args.raw: (string) The current tag's unprocessed argument string. Equivalent in function to <MacroContext>.args.raw.contents: (string) The current tag's contents—i.e. the text between the current tag and the next.v2.0.0
<MacroContext>.self → objectThe macro's definition—created via Macro.add().
v2.0.0
<MacroContext>.contextHas(filter) → booleanReturns whether any of the macro's ancestors passed the test implemented by the given filter function.
v2.0.0filter: (function) The function used to test each ancestor execution context object, which is passed in as its sole parameter.var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextHas(includeAncestor); → Returns true if any ancestor was an <<include>> macro
<MacroContext>.contextSelect(filter) → null | objectReturns the first of the macro's ancestors which passed the test implemented by the given filter function or null, if no members pass.
v2.0.0filter: (function) The function used to test each ancestor execution context object, which is passed in as its sole parameter.var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextSelect(includeAncestor); → Returns the first <<include>> macro ancestor
<MacroContext>.contextSelectAll(filter) → object arrayReturns a new array containing all of the macro's ancestors which passed the test implemented by the given filter function or an empty array, if no members pass.
v2.0.0filter: (function) The function used to test each ancestor execution context object, which is passed in as its sole parameter.var includeAncestor = function (ctx) { return ctx.name === "include"; };
this.contextSelectAll(includeAncestor); → Returns an array of all <<include>> macro ancestors
<MacroContext>.error(message) → boolean:falseRenders the message prefixed with the name of the macro and returns false.
v2.0.0message: (string) The error message to output.// Given: <<someMacro …>>
return this.error("oops"); → Outputs '<<someMacro>>: oops'
Passage APIInstances of the Passage object are returned by the Story.get() static method.
All properties of Passage objects should be treated as if they were read-only, as modifying them could result in unexpected behavior.
<Passage>.domId → stringThe DOM ID of the passage (created from the slugified passage title).
v2.0.0
<Passage>.tags → string arrayThe tags of the passage.
v2.0.0
<Passage>.text → stringThe raw text of the passage.
v2.0.0
<Passage>.title → stringThe title of the passage.
v2.0.0
<Passage>.description() → stringReturns the description of the passage (created from either an excerpt of the passage or the Config.passages.descriptions object).
v2.0.0Story.get("The Ducky").description() → Returns the description of "The Ducky" passage
<Passage>.processText() → stringReturns the text of the Passage object (similar to <Passage>.text) after applying nobr tag and image passage processing to it.
v2.0.0Story.get("The Ducky").processText() → Returns the fully processed text of "The Ducky" passage
Save APINOTE: There are several configuration settings for saves, which it would be wise to familiarize yourself with.
NOTE: Adding additional properties directly to save objects is not recommended. Instead, use the metadata property.
Save objects have some of the following properties:
id: (string) The story's save ID.state: (object) The marshaled story history (see below for details).title: (string) The title of the save.date: (integer) The date when the save was created (in milliseconds elapsed since epoch).metadata: (optional, any) Save metadata (end-user specified; must be JSON-serializable).version: (optional, any) Save version (end-user specified via Config.saves.version).The state object has the following properties:
history: (array) The array of moment objects (see below for details).index: (integer) The index of the active moment.expired: (optional, array) The array of expired moment passage titles, exists only if any moments have expired.seed: (optional, string) The seed of SugarCube's seedable PRNG, exists only if enabled.Each moment object has the following properties:
title: (string) The title of the associated passage.variables: (object) The current variable store object, which contains sigil-less name ⇒ value pairs (e.g. $foo exists as foo).pull: (optional, integer) The current pull count of SugarCube's seedable PRNG, exists only if enabled.
Save.clear()Deletes all slot saves and the autosave, if it's enabled.
v2.0.0Save.clear()
Save.get()Returns the saves object.
v2.0.0Save.get()
Save.ok() → booleanReturns whether both the slot saves and autosave are available and ready.
v2.0.0if (Save.ok()) {
/* Code to manipulate saves. */
}
Save.slots.length → integerReturns the total number of available slots.
v2.0.0Save.slots.length
Save.slots.count() → integerReturns the total number of filled slots.
v2.0.0Save.slots.count()
Save.slots.delete(slot)Deletes a save from the given slot.
v2.0.0slot: (integer) Save slot index (0-based).Save.slots.delete(5) → Deletes the sixth slot save
Save.slots.get(slot) → objectReturns a save object from the given slot or null, if there was no save in the given slot.
v2.0.0slot: (integer) Save slot index (0-based).Save.slots.get(5) → Returns the sixth slot save
Save.slots.has(slot) → booleanReturns whether the given slot is filled.
v2.0.0slot: (integer) Save slot index (0-based).if (Save.slots.has(5)) {
/* Code to manipulate the sixth slot save. */
}
Save.slots.isEmpty() → booleanReturns whether there are any filled slots.
v2.0.0Save.slots.isEmpty() → Effectively returns: Save.slots.count() === 0
Save.slots.load(slot)Loads a save from the given slot.
v2.0.0slot: (integer) Save slot index (0-based).Save.slots.load(5) → Load the sixth slot save
Save.slots.ok() → booleanReturns whether the slot saves are available and ready.
v2.0.0if (Save.slots.ok()) {
/* Code to manipulate slot saves. */
}
Save.slots.save(slot [, title [, metadata]])Saves to the given slot.
v2.0.0slot: (integer) Save slot index (0-based).title: (optional, string) The title of the save. If omitted or null, defaults to the passage's description.metadata: (optional, any) The data to be stored in the save object's metadata property. Must be JSON-serializable.→ Save to the sixth slot save with the default title and no metadata
Save.slots.save(5)
→ Save to the sixth slot save with the title "Midgar" and no metadata
Save.slots.save(5, "Midgar")
→ Save to the sixth slot save with the default title and metadata someMetadata
Save.slots.save(5, null, someMetadata)
→ Save to the sixth slot save with the title "Midgar" and metadata someMetadata
Save.slots.save(5, "Midgar", someMetadata)
Save.autosave.delete()Deletes the autosave.
v2.0.0Save.autosave.delete() → Deletes the autosave
Save.autosave.get() → objectReturns the save object from the autosave or null, if there was no autosave.
v2.0.0Save.autosave.get() → Returns the autosave
Save.autosave.has() → booleanReturns whether the autosave is filled.
v2.0.0if (Save.autosave.has()) {
/* Code to manipulate the autosave. */
}
Save.autosave.load()Loads the autosave.
v2.0.0Save.autosave.load() → Load the autosave
Save.autosave.ok() → booleanReturns whether the autosave is available and ready.
v2.0.0if (Save.autosave.ok()) {
/* Code to manipulate the autosave. */
}
Save.autosave.save([title [, metadata]])Saves to the autosave.
v2.0.0title: (optional, string) The title of the save. If omitted or null, defaults to the passage's description.metadata: (optional, any) The data to be stored in the save object's metadata property. Must be JSON-serializable.→ Save to the autosave with the default title and no metadata
Save.autosave.save()
→ Save to the autosave with the title "Midgar" and no metadata
Save.autosave.save("Midgar")
→ Save to the autosave with the default title and metadata someMetadata
Save.autosave.save(null, someMetadata)
→ Save to the autosave with the title "Midgar" and metadata someMetadata
Save.autosave.save("Midgar", someMetadata)
Save.export([filename [, metadata]])Saves to disk.
v2.0.0: Basic syntax.v2.8.0: Added metadata parameter.filename: (optional, string) The base filename of the save, which gets slugified to remove most symbols. Appended to this is a datestamp (format: YYYMMDD-hhmmss) and the file extension .save. (e.g. "The Scooby Chronicles" would result in the full filename: the-scooby-chronicles-{datestamp}.save). If omitted or null, defaults to the story's title.metadata: (optional, any) The data to be stored in the save object's metadata property. Must be JSON-serializable.→ Export a save with the default filename and no metadata
Save.export()
→ Export a save with the filename "the-7th-fantasy-{datestamp}.save" and no metadata
Save.export("The 7th Fantasy")
→ Export a save with the default filename and metadata someMetadata
Save.export(null, someMetadata)
→ Export a save with the filename "the-7th-fantasy-{datestamp}.save" and metadata someMetadata
Save.export("The 7th Fantasy", someMetadata)
Save.import(event)Loads a save from disk.
NOTE: You do not call this manually, it must be called by the change event handler of an <input type="file"> element.
v2.0.0event: (event object) The event object which was passed to the change event handler of the associated <input type="file"> element.// Add file input
var input = document.createElement('input');
input.type = 'file';
input.id = 'saves-import-file';
input.name = 'saves-import-file';
// Set up Save.import() as the event handler for when a file has been chosen
jQuery(input).on('change', Save.import);
In case you needed to do more than simply load the save, you may do something like the following:
// Add file input
var input = document.createElement('input');
input.type = 'file';
input.id = 'saves-import-file';
input.name = 'saves-import-file';
// Set up a custom event handler for when a file has been chosen, which will call Save.import()
jQuery(input).on('change', function (ev) {
// You must pass in the event when calling Save.import() manually
Save.import(ev);
// Put anything else you needed to do here
});
Save.serialize([metadata]) → string | nullReturns a save as a serialized string, or null if saving is not allowed within the current context.
v2.21.0metadata: (optional, any) The data to be stored as metadata. Must be JSON-serializable.→ Serialize a save with no metadata
const myGameState = Save.serialize();
if (myGameState === null) {
/* Failure. You've disallowed saving. */
}
→ Serialize a save with metadata someMetadata
const myGameState = Save.serialize(someMetadata);
if (myGameState === null) {
/* Failure. You've disallowed saving. */
}
Save.deserialize(saveStr) → any | nullDeserializes the given save string, created via Save.serialize(), and loads the save. Returns the bundled metadata, if any, or null if the given save could not be deserialized and loaded.
v2.21.0saveStr: (string) The serialized save string.→ Deserialize a save with no metadata
const loadResult = Save.deserialize(myGameState);
if (loadResult === null) {
/* Failure. An error was displayed to the player. */
}
→ Deserialize a save with metadata
const loadResult = Save.deserialize(myGameState);
if (loadResult !== null) {
/* Success. Do something with loadResult, which contains the metadata. */
}
else {
/* Failure. An error was displayed to the player. */
}
Setting APIManages the Settings dialog and settings object.
Setting.addHeader(name [, desc])Adds a header to the Settings dialog.
v2.7.1name: (string) Name of the header.desc: (optional, string) Description explaining the header in greater detail.// Setting up a basic header
Setting.addHeader("Content Settings");
// Setting up a header w/ a description
Setting.addHeader("Content Settings", "Settings controlling what content is made available in the game.");
Setting.addToggle(name, definition)Adds the named property to the settings object and a toggle control for it to the Settings dialog.
v2.0.0: Basic syntax.v2.26.0: Added desc property to definition object.name: (string) Name of the settings property to add, which the control will manage.definition: (object) Definition of the control.A toggle definition object should have some of the following properties:
label: (string) Label to use for the control.desc: (optional, string) Description explaining the control in greater detail.default: (optional, boolean) The default value for the setting and default state of the control. Leaving it undefined means to use false as the default.onInit: (optional, function) The function to call during initialization.onChange: (optional, function) The function to call when the control's state is changed.// Setting up a basic toggle control for the settings property 'mature'
Setting.addToggle("mature", {
label : "Content for mature audiences?"
}); // default value not defined, so false is used
// Setting up a toggle control for the settings property 'widescreen' w/ callbacks
var settingWidescreenHandler = function () {
if (settings.widescreen) { // is true
$("html").addClass("widescreen");
}
else { // is false
$("html").removeClass("widescreen");
}
};
Setting.addToggle("widescreen", {
label : "Allow the story to use the full width of your browser window?",
default : false,
onInit : settingWidescreenHandler,
onChange : settingWidescreenHandler
});
Setting.addList(name, definition)Adds the named property to the settings object and a list control for it to the Settings dialog.
v2.0.0: Basic syntax.v2.26.0: Added desc property to definition object.name: (string) Name of the settings property to add, which the control will manage.definition: (object) Definition of the control.A list definition object should have some of the following properties:
label: (string) Label to use for the control.list: (array) The array of items.desc: (optional, string) Description explaining the control in greater detail.default: (optional, [as list array]) The default value for the setting and default state of the control. It should have the same value as one of the members of the list array. Leaving it undefined means to use the first array member as the default.onInit: (optional, function) The function to call during initialization.onChange: (optional, function) The function to call when the control's state is changed.// Setting up a basic list control for the settings property 'difficulty'
Setting.addList("difficulty", {
label : "Choose a difficulty level.",
list : ["Easy", "Normal", "Hard", "INSANE"],
default : "Normal"
});
// Setting up a list control for the settings property 'theme' w/ callbacks
var settingThemeNames = ["(none)", "Bright Lights", "Charcoal", "Midnight", "Tinsel City"];
var settingThemeHandler = function () {
// cache the jQuery-wrapped <html> element
var $html = $("html");
// remove any existing theme class
$html.removeClass("theme-bright-lights theme-charcoal theme-midnight theme-tinsel-city");
// switch on the theme name to add the requested theme class
switch (settings.theme) {
case "Bright Lights":
$html.addClass("theme-bright-lights");
break;
case "Charcoal":
$html.addClass("theme-charcoal");
break;
case "Midnight":
$html.addClass("theme-midnight");
break;
case "Tinsel City":
$html.addClass("theme-tinsel-city");
break;
}
};
Setting.addList("theme", {
label : "Choose a theme.",
list : settingThemeNames,
onInit : settingThemeHandler,
onChange : settingThemeHandler
}); // default value not defined, so the first array member "(none)" is used
Setting.addRange(name, definition)Adds the named property to the settings object and a range control for it to the Settings dialog.
v2.26.0name: (string) Name of the settings property to add, which the control will manage.definition: (object) Definition of the control.A range definition object should have some of the following properties:
label: (string) Label to use for the control.min: (number) The minimum value.max: (number) The maximum value.step: (number) Limits the increments to which the value may be set. It must be evenly divisible into the full range—i.e. max - min.desc: (optional, string) Description explaining the control in greater detail.default: (optional, number) The default value for the setting and default state of the control. Leaving it undefined means to use the value of max as the default.onInit: (optional, function) The function to call during initialization.onChange: (optional, function) The function to call when the control's state is changed.// Setting up a volume control for the settings property 'masterVolume' w/ callback
Setting.addRange("masterVolume", {
label : "Master volume.",
min : 0,
max : 10,
step : 1,
onChange : function () {
$.wiki("<<masteraudio volume " + (settings["masterVolume"] / 10) + ">>");
}
}); // default value not defined, so max value (10) is used
Setting.load()Loads the settings from storage.
NOTE: The API automatically calls this method at startup, so you should never need to call this method manually.
v2.0.0Setting.load();
Setting.reset([name])Resets the setting with the given name to its default value. If no name is given, resets all settings.
v2.0.0name: (optional, string) Name of the settings object property to reset.// Reset the setting 'difficulty'
Setting.reset("difficulty");
// Reset all settings
Setting.reset();
Setting.save()Saves the settings to storage.
NOTE: The controls of the Settings dialog automatically call this method when settings are changed, so you should normally never need to call this method manually. Only when manually modifying the values of settings object properties, outside of the controls, would you need to call this method.
v2.0.0Setting.save();
settings objectA prototype-less generic object whose properties and values are defined by the Setting.addToggle(), Setting.addList(), and Setting.addRange() methods.
Normally, the values of its properties are automatically managed by their associated Settings dialog control. If necessary, however, you may manually change their values—n.b. you'll need to call the Setting.save() after having done so.
v2.0.0State APIThe history contains all moments (states) created during play. Since it is possible to navigate the history (move backward and forward though the moments within the history), the history may contain both past moments (i.e. moments which have been played) and future moments (i.e. moments which had been played, but have been rewound/undone, yet are still available to be restored). Additionally, there is the active moment (i.e. present) and expired moments (i.e. moments which had been played, but have expired from the history, thus cannot be navigated to).
API members dealing with the history work upon either the active moment (i.e. present), or one of the history subsets: the full in-play history (i.e. past + future), the past in-play subset (i.e. past only), or the extended past subset (i.e. expired + past). These instances will be noted.
State.active → objectReturns the active (present) moment.
NOTE: Using State.active directly is generally unnecessary as there exist a number of shortcut properties, State.passage and State.variables, and story functions, passage() and variables(), which grant access to its normal properties.
v2.0.0State.active.title → The title of the present moment
State.active.variables → The variables of the present moment
State.bottom → objectReturns the bottommost (least recent) moment from the full in-play history (past + future).
v2.0.0State.bottom.title → The title of the least recent moment within the full in-play history
State.bottom.variables → The variables of the least recent moment within the full in-play history
State.current → objectReturns the current moment from the full in-play history (past + future), which is the pre-play version of the active moment.
WARNING: State.current is not a synonym for State.active. You will, very likely, never need to use State.current directly within your code.
v2.8.0State.current.title → The title of the current moment within the full in-play history
State.current.variables → The variables of the current moment within the full in-play history
State.length → integerReturns the number of moments within the past in-play history (past only).
v2.0.0if (State.length === 0) {
/* No moments within the past in-play history. Egad! */
}
State.passage → stringReturns the title of the passage associated with the active (present) moment.
v2.0.0State.passage → The passage title of the present moment
State.temporary → objectReturns the current temporary variables.
v2.13.0State.temporary → The current temporary variables
State.size → integerReturns the number of moments within the full in-play history (past + future).
v2.0.0if (State.size === 0) {
/* No moments within the full in-play history. Egad! */
}
State.top → objectReturns the topmost (most recent) moment from the full in-play history (past + future).
WARNING: State.top is not a synonym for State.active. You will, very likely, never need to use State.top directly within your code.
v2.0.0State.top.title → The title of the most recent moment within the full in-play history
State.top.variables → The variables of the most recent moment within the full in-play history
State.turns → integerReturns the total number of played moments within the extended past history (expired + past).
v2.0.0if (State.turns === 1) {
/* Initial turn. The starting passage is displayed. */
}
State.variables → objectReturns the variables from the active (present) moment.
v2.0.0State.variables → The variables of the present moment
State.getVar(varName) → anyReturns the value of the story or temporary variable by the given name.
v2.22.0varName: (string) The name of the story or temporary variable, including its sigil—e.g. $charName.State.getVar("$charName") → Returns the value of $charName
State.has(passageTitle) → booleanReturns whether any moments with the given title exist within the past in-play history (past only).
NOTE: State.has() does not check expired moments. If you need to know if the player has ever been to a particular passage, then you must use the State.hasPlayed() method or the hasVisited() story function.
v2.0.0passageTitle: (string) The title of the moment whose existence will be verified.State.has("The Ducky") → Returns whether a moment matching "The Ducky" exists
State.hasPlayed(passageTitle) → booleanReturns whether any moments with the given title exist within the extended past history (expired + past).
NOTE: If you need to check for multiple passages, the hasVisited() story function will likely be more convenient to use.
v2.0.0passageTitle: (string) The title of the moment whose existence will be verified.State.hasPlayed("The Ducky") → Returns whether a moment matching "The Ducky" ever existed
State.index(index) → objectReturns the moment, relative to the bottom of the past in-play history (past only), at the given index.
v2.0.0index: (integer) The index of the moment to return.State.index(0) → Returns the least recent moment within the past in-play history
State.index(1) → Returns the second to least recent moment within the past in-play history
State.index(State.length - 1) → Returns the most recent moment within the past in-play history
State.initPRNG([seed [, useEntropy]])Initializes the seedable pseudo-random number generator (PRNG) and integrates it into the story state and saves. Once initialized, the State.random() method and story functions, random() and randomFloat(), return results from the seeded PRNG (by default, they return results from Math.random()).
NOTE: State.initPRNG() must be called during story initialization, within either a script section (Twine 2: the Story JavaScript, Twine 1/Twee: a script-tagged passage) or the StoryInit special passage. Additionally, it is recommended that you do not specify any arguments to State.initPRNG() and allow it to automatically seed itself. If you should chose to use an explicit seed, however, it is strongly recommended that you also enable additional entropy, otherwise all playthroughs for all players will be exactly the same.
v2.0.0seed: (optional, string) The explicit seed used to initialize the pseudo-random number generator.useEntropy: (optional, boolean) Enables the use of additional entropy to pad the specified explicit seed.State.initPRNG() → Automatically seed the PRNG (recommended)
State.initPRNG("aVeryLongSeed") → Seed the PRNG with "aVeryLongSeed"
State.initPRNG("aVeryLongSeed", true) → Seed the PRNG with "aVeryLongSeed" and pad it with extra entropy
State.isEmpty() → booleanReturns whether the full in-play history (past + future) is empty.
v2.0.0if (State.isEmpty()) {
/* No moments within the full in-play history. Egad! */
}
State.peek([offset]) → objectReturns the moment, relative to the top of the past in-play history (past only), at the, optional, offset.
v2.0.0offset: (optional, integer) The offset, from the top of the past in-play history, of the moment to return. If not given, an offset of 0 is used.State.peek() → Returns the most recent moment within the past in-play history
State.peek(0) → Returns the most recent moment within the past in-play history
State.peek(1) → Returns the second most recent moment within the past in-play history
State.peek(State.length - 1) → Returns the least recent moment within the past in-play history
State.random() → numberReturns a pseudo-random real number (floating-point) in the range 0 (inclusive) up to, but not including, 1 (exclusive).
NOTE: By default, it simply returns results from Math.random(), however, when the seedable PRNG has been enabled, via State.initPRNG(), it returns results from the seeded PRNG instead.
v2.0.0State.random() → Returns a pseudo-random floating-point number in the range [0, 1)
State.setVar(varName, value) → booleanSets the value of the story or temporary variable by the given name. Returns whether the operation was successful.
v2.22.0varName: (string) The name of the story or temporary variable, including its sigil—e.g. $charName.value: (any) The value to assign.State.setVar("$charName", "Jane Doe") → Assigns the string "Jane Doe" to $charName
Story API
Story.domId → stringThe DOM ID of the story (created from the slugified story title).
v2.0.0
Story.title → stringThe title of the story.
v2.0.0
Story.get(passageTitle) → Passage objectReturns the Passage object referenced by the given title, or an empty Passage object on failure.
v2.0.0passageTitle: (string) The title of the Passage object to return.Story.get("The Ducky") → Returns the Passage object matching "The Ducky"
Story.has(passageTitle) → booleanReturns whether a Passage object referenced by the given title exists.
v2.0.0passageTitle: (string) The title of the Passage object whose existence will be verified.Story.has("The Ducky") → Returns whether a Passage object matching "The Ducky" exists
Story.lookup(propertyName , searchValue [, sortProperty]) → Passage object arrayReturns an array of Passage objects each of which must contain a property matching the given name, whose value matches the given needle, or an empty array, if no matches are made.
v2.0.0propertyName: (string) The name of property whose value will be compared to the search value.searchValue: (string | number) The value to search for within the matched property. The type of the property determines how the search occurs; direct comparison for non-arrays, while arrays are iterated over. If the property value, for non-arrays, or any of the property members' values, for arrays, match, then the Passage object is added to the results array.sortProperty: (optional, string) The property whose value will be used to lexicographically sort the returned array. If not given, the Passage object's title property is used.→ Returns all 'forest'-tagged Passage objects, sorted by their titles
Story.lookup("tags", "forest");
Story.lookupWith(filter [, sortProperty]) → Passage object arrayReturns an array of Passage objects which passed the test implemented by the given filter function or an empty array, if no objects pass.
v2.11.0filter: (function) The function used to test each Passage object, which is passed in as its sole parameter. If the function returns true, then the Passage object is added to the results array.sortProperty: (optional, string) The property whose value will be used to lexicographically sort the returned array. If not given, the Passage object's title property is used.→ Returns all Passage objects whose titles contain whitespace, sorted by their titles
var hasWhitespaceRegExp = /\s/;
Story.lookupWith(function (p) {
return hasWhitespaceRegExp.test(p.title);
});
UI API
UI.alert(message [, options [, closeFn]])Opens the built-in alert dialog, displaying the given message to the player.
v2.0.0message: (string) The message to display to the player.options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.UI.alert("You smell of elderberries!");
UI.jumpto([options [, closeFn]])Opens the built-in jump to dialog, which is populated via the bookmark tag.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.UI.jumpto();
UI.restart([options])Opens the built-in restart dialog, prompting the player to restart the story.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.UI.restart();
UI.saves([options [, closeFn]])Opens the built-in saves dialog.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.UI.saves();
UI.settings([options [, closeFn]])Opens the built-in settings dialog, which is populated from the Setting API.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.UI.settings();
UI.share([options [, closeFn]])Opens the built-in share dialog, which is populated from the StoryShare passage.
v2.0.0options: (optional, null | object) The options object. See Dialog.addClickHandler() for more information.closeFn: (optional, null | function) The function to execute whenever the dialog is closed.UI.share();
UIBar API
UIBar.destroy()Completely removes the UI bar and all of its associated styles and event handlers.
v2.17.0UIBar.destroy();
UIBar.stow()Stows the UI bar, so that it takes up less space.
v2.17.0UIBar.stow();
UIBar.unstow()Unstows UI bar, so that it is fully accessible again.
v2.17.0UIBar.unstow();
This is a collection of tips, from how-tos to best practices.
Suggestions for new entries may be submitted by creating a new issue at SugarCube's code repository. NOTE: Acceptance of submissions is not guaranteed.
When you have a situation where you're using a set of passages as some kind of menu/inventory/etc and it's possible for the player to interact with several of those passages, or even simply the same one multiple times, then returning them to the passage they were at before entering the menu can be problematic as they're possibly several passages removed from that originating passage—thus, the <<return>> macro and link constructs like [[Return|previous()]] will not work.
The most common way to resolve this arbitrarily long return issue is to use a bit of JavaScript to record the last non-menu passage the player visited into a story variable and then to create a link with that.
For example, the code to record the last non-menu passage: (Twine 2: the Story JavaScript, Twine 1/Twee: a script-tagged passage)
predisplay['set-return-variable'] = function () {
if (!tags().includes('noreturn')) {
State.variables['return'] = passage();
}
};
You'll need to tag each and every one of your menu passages with noreturn—you may use any tag you wish (e.g. menu, inventory), just ensure you change the name in the code if you decide upon another. If necessary, you may also use multiple tags by switching from <Array>.includes() to <Array>.includesAny() in the above example.
In your menu passages, your long return links will simply reference the $return story variable, like so:
→ Using link markup
[[Return|$return]]
→ Using <<link>> macro (separate argument form)
<<link "Return" $return>><</link>>
NOTE (Twine 2): Unfortunately, due to how the Twine 2 automatic passage creation feature currently works, using the link markup form will cause a passage named $return to be created which will need to be deleted. To avoid this problem, it's suggested that you use the separate argument form of <<link>> in Twine 2—as shown above.
As a basic working definition, non-generic object types—a.k.a. classes—are instantiable objects whose prototype is not Object—e.g. Array is a native non-generic object type.
Most of the commonly used native non-generic object types are already fully compatible with and supported for use within story variables—e.g. Array, Date, Map, and Set. Non-native/custom non-generic object types, on the other hand, must be made compatible to be successfully stored within story variables.
Making custom non-generic object types fully compatible requires that two methods be added to their prototype, .clone() and .toJSON(), to support cloning—i.e. deep copying—instances of the type.
.clone() method needs to return a clone of the instance..toJSON() method needs to return a code string that when evaluated will return a clone of the instance.In both cases, since the end goal is roughly the same, this means creating a new instance of the base object type and populating it with clones of the original instance's data. There is no one size fits all example for either of these methods because an instance's properties, and the data contained therein, are what determine what you need to do.
Here's a simple example whose constructor takes a single config/option object parameter:
window.ContactInfo = function (config) {
// Set up our own data properties with some defaults.
this.name = '';
this.address = '';
this.phone = '';
this.email = '';
// Clone the given config object's own properties into our own properties.
//
// NOTE: We use the SugarCube built-in `clone()` function to make deep
// copies of each of the properties' values.
Object.keys(config).forEach(function (pn) {
this[pn] = clone(config[pn]);
}, this);
};
ContactInfo.prototype.clone = function () {
// Return a new instance containing our own data.
return new ContactInfo(this);
};
ContactInfo.prototype.toJSON = function () {
// Return a code string that will create a new instance containing our
// own data.
//
// NOTE: Supplying `this` directly as the `reviveData` parameter to the
// `JSON.reviveWrapper()` call will trigger out of control recursion in
// the serializer, so we must pass it a clone of our own data instead.
var ownData = {};
Object.keys(this).forEach(function (pn) {
ownData[pn] = clone(this[pn]);
}, this);
return JSON.reviveWrapper('new ContactInfo($ReviveData$)', ownData);
};
Creating a new instance of this ContactInfo example would be something like:
<<set $Joe to new ContactInfo({
name : 'Joe Blow',
phone : '1 555 555 1212',
email : 'joe@blow.com'
})>>
Here's a simple example whose constructor takes multiple discrete parameters:
window.ContactInfo = function (name, addr, phone, email) {
// Set up our own data properties with the given values or defaults.
this.name = name || '';
this.address = addr || '';
this.phone = phone || '';
this.email = email || '';
};
ContactInfo.prototype.clone = function () {
// Return a new instance containing our own data.
return new ContactInfo(
this.name,
this.address,
this.phone,
this.email
);
};
ContactInfo.prototype.toJSON = function () {
// Return a code string that will create a new instance containing our
// own data.
return JSON.reviveWrapper(String.format(
'new ContactInfo({0},{1},{2},{3})',
JSON.stringify(this.name),
JSON.stringify(this.address),
JSON.stringify(this.phone),
JSON.stringify(this.email)
));
};
Creating a new instance of this ContactInfo example would be something like:
<<set $Joe to new ContactInfo(
'Joe Blow',
'',
'1 555 555 1212',
'joe@blow.com'
)>>
Here's a simple example whose constructor takes multiple discrete parameters, but also includes an ._init() helper method to allow the .clone() and .toJSON() methods to require less manual tinkering than the previous discrete parameters example by automatically copying an instance's own data:
window.ContactInfo = function (name, addr, phone, email) {
// Set up our own data properties with the given values or defaults.
this.name = name || '';
this.address = addr || '';
this.phone = phone || '';
this.email = email || '';
};
ContactInfo.prototype._init = function (obj) {
// Clone the given object's own properties into our own properties.
Object.keys(obj).forEach(function (pn) {
this[pn] = clone(obj[pn]);
}, this);
// Return `this` to make usage easier.
return this;
};
ContactInfo.prototype.clone = function () {
// Return a new instance containing our own data.
return (new ContactInfo())._init(this);
};
ContactInfo.prototype.toJSON = function () {
// Return a code string that will create a new instance containing our
// own data.
//
// NOTE: Supplying `this` directly as the `reviveData` parameter to the
// `JSON.reviveWrapper()` call will trigger out of control recursion in
// the serializer, so we must pass it a clone of our own data instead.
var ownData = {};
Object.keys(this).forEach(function (pn) {
ownData[pn] = clone(this[pn]);
}, this);
return JSON.reviveWrapper('(new ContactInfo())._init($ReviveData$)', ownData);
};
Creating a new instance of this ContactInfo example would be something like:
<<set $Joe to new ContactInfo(
'Joe Blow',
'',
'1 555 555 1212',
'joe@blow.com'
)>>
Media passages are simply a way to embed media into your project—specially tagged passages that contain the data URI of a Base64-encoded media source. Audio, image, video, and VTT passages are supported.
For example, the following is the data URI of a Base64-encoded PNG image of a red dot ():
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHE
lEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
v2.0.0: Image passages.v2.24.0: Added audio, video, and VTT passages.
Generally, it's expected that you will use a compiler which supports the automatic creation of media passages, however, they may be created manually.
Compilers supporting automatic creation of media passages:
Warning (Twine 2): Due to various limitations in its design, if you're using Twine 2 as your IDE/compiler, then it is strongly recommended that you do not create more than a few media passages and definitely do not use large sources.
To manually create a media passage:
See the MDN article Media formats for HTML audio and video for more information on formats commonly supported in browsers—pay special attention to the Browser compatibility section.
NOTE: As with all special tags, media passage tags are case sensitive, so their spelling and capitalization must be exactly as shown.
| Passage type | Tag |
|---|---|
| Audio passage | Twine.audio |
| Image passage | Twine.image |
| Video passage | Twine.video |
| VTT passage | Twine.vtt |
v2.2.0
In test mode, SugarCube will wrap all macros, and some non-macro markup (e.g. link & image markup), within additional HTML elements, called "debug views" ("views" for short). Views make their associated code visible, thus providing onscreen feedback—they may also be hovered over which, generally, exposes additional information about the underlying code.
WARNING: Because of the additional HTML elements added by the debug views, some nested markup and selectors may be broken. This only affects test mode.
In versions of SugarCube ≥v2.23.0, the debugging interface offers additional tools, namely variable watches and arbitrary history navigation.
To enable test mode, use the testing option (-t, --test).
To enable test mode from the Stories screen, click on the story's gear menu and select the Test Story menu item.
To enable test mode from the story editor/map screen, click on the Test menu item (right side of the bottom bar).
To enable test mode from the story editor/map screen while starting at a specific passage, hover over a passage and select the menu item.
To enable test mode from the Stories screen, click on the story's gear menu and select the Test Play menu item.
To enable test mode from the story editor/map screen, click on the Test menu item (right side of the bottom bar).
To enable test mode from the story editor/map screen while starting at a specific passage, hover over a passage and select the menu item.
To enable test mode while starting at a specific passage, right-click on a passage and select the Test Play From Here context menu item.
NOTE: Unfortunately, due to limitations in the current release of Twine 1, the Build menu's Test Play menu item is not able to trigger test mode. You may, however, simply use the Test Play From Here context menu item on the Start passage to achieve the same result.
You may forcibly enable test mode manually by setting the Config object's debug property to true. For example:
Config.debug = true; // forcibly enable test mode
See Config.debug for more information.
The debug bar (bottom right corner of the page) allows you to: watch the values of story and temporary variables, toggle the debug views, and jump to any moment/turn within the history.
The variable watch panel may be toggled via the Watch button. To add a watch for a variable, type its name into the Add field and then either press enter/return or click the button—n.b. depending on the age of your browser, you may also see a list of all current variables when interacting with the Add field. To delete a watch, click the button next to its name in the watch panel. To add watches for all current variables, click the button. To delete all current watches, click the button.
The debug views may be toggled via the Views button.
To jump to any moment/turn within the available history, select the moment/turn from the Turn select field.
The debug views themselves may be toggled on and off (default: on) via the Debug View button (top of the UI bar).
If you've removed/hidden the UI bar, a construct like the following will allow you to toggle the views on and off:
<<button "Toggle Debug Views">><<script>>DebugView.toggle()<</script>><</button>>
NOTE: That only toggles the views, test mode must still be enabled first.
This is a reference on how to install SugarCube in Tweego, Twine 2, and Twine 1/Twee.
NOTE (Twine 2): Newer versions of Twine 2 come bundled with a version of SugarCube v2, so you only need to read these instructions if you want to install a newer version of SugarCube v2 than is bundled or a non-standard release.
See Tweego's documentation for more information.
There are two primary branches of Twine 2 as far as SugarCube is concerned:
Regardless of the version of Twine 2 you're using, follow these instructions to install a local copy of SugarCube v2:
Formats link in the Twine 2 sidebar.Add a New Format tab.format.js file, based upon the path from step #2, into the textbox and click the +Add button (see below for examples).NOTE: If constructing the file URL from a shell path, ensure that either it does not contain escapes or you properly convert them into the correct URL percent-encoded form.
If the full path to the contents of the archive is something like:
/home/soandso/Twine/StoryFormats/SugarCube-2/format.js
Then the file URL to it would be:
file:///home/soandso/Twine/StoryFormats/SugarCube-2/format.js
If the full path to the contents of the archive is something like:
C:\Users\soandso\Documents\Twine\StoryFormats\SugarCube-2\format.js
Then the file URL to it would be (note the changed slashes):
file:///C:/Users/soandso/Documents/Twine/StoryFormats/SugarCube-2/format.js
Follow these instructions to install a local copy of SugarCube v2:
targets directory within.targets directory and extract it, included directory and all.If you followed the steps correctly, within Twine 1/Twee's targets directory you should now have a sugarcube-2 directory, which contains several files (e.g. header.html, sugarcube-2.py, etc).
NOTE: Due to a flaw in the current release of Twine 1/Twee (v1.4.2), if you rename the directory included in the archive (or simply copy its contents to your current SugarCube v2 install), then you must ensure that the file with the extension .py (the story format's custom Twine 1 Header class file) within is named the same as the directory (i.e. the name of the directory and .py file must match).
For example, if the name of SugarCube's directory is sugarcube, then the name of the .py file within must be sugarcube.py. Similarly, if the directory is sugarcube-2, then the name of the .py file within must be sugarcube-2.py. Etc.
The directory and .py file names within the archive available for download are already properly matched—as sugarcube-2 and sugarcube-2.py—and to avoid issues it recommended that you simply do not rename them.
This is a reference on how to upgrade existing SugarCube code to work with newer versions of SugarCube.
NOTE: The vast majority of newer SugarCube versions do not have any changes that would require an upgrade. For those versions that do, the upgrades are normally completely elective and may be addressed at your leisure, or not at all. Sometimes there are breaking changes, however, and these must be addressed immediately.
All changes within this version are elective changes which you may address at your leisure.
| Macro | Changes |
|---|---|
<<display>> |
The <<display>> macro has been replaced by the <<include>> macro. |
All changes within this version are elective changes which you may address at your leisure.
strings objectThe strings API object has been replaced by the l10nStrings object. See the Localization guide for more information.
All changes within this version are elective changes which you may address at your leisure.
| Macro | Changes |
|---|---|
<<click>> |
The <<click>> macro has been deprecated in favor of the <<link>> macro. |
<<playlist>> |
The <<playlist>> macro has had its argument list changed, for compatibility with <<createplaylist>>. See the <<playlist>> macro for more information. |
<<setplaylist>> |
The <<setplaylist>> macro has been deprecated in favor of the <<createplaylist>> macro. |
<<stopallaudio>> |
The <<stopallaudio>> macro has been deprecated in favor of <<audio ":all" stop>>. See the <<audio>> macro for more information. |
All changes within this version are elective changes which you may address at your leisure.
State APISeveral State API methods have moved to the new Engine API. See the Engine API docs for more information.
Old State method |
New Engine method |
|---|---|
State.backward() |
Engine.backward() |
State.display() |
Engine.display() |
State.forward() |
Engine.forward() |
State.play() |
Engine.play() |
State.restart() |
Engine.restart() |
State.show() |
Engine.show() |
UI APISeveral UI API methods have moved to the new Dialog API. See the Dialog API docs for more information.
Old UI method |
New Dialog method |
|---|---|
UI.addClickHandler() |
Dialog.addClickHandler() |
UI.body() |
Dialog.body() |
UI.close() |
Dialog.close() |
UI.isOpen() |
Dialog.isOpen() |
UI.open() |
Dialog.open() |
UI.setup() |
Dialog.setup() |
config APIThe config API has been renamed Config for better consistency with the other APIs.
WARNING: All changes within this version are breaking changes that you must address immediately.
The HTML & CSS have undergone significant changes. See the HTML and CSS docs for more information.
| Passage | Changes |
|---|---|
MenuOptions |
The MenuOptions special passage has been removed. See the Options system section for more information. |
MenuShare |
The MenuShare special passage has been removed. Instead, use the StoryShare special passage. |
MenuStory |
The MenuStory special passage has been removed. Instead, use the StoryMenu special passage. |
config objectThe config object has been renamed to Config and some of its properties have also changed. See the Config API docs for more information.
| Property | Changes |
|---|---|
config.altPassageDescription |
Changed the config.altPassageDescription property to Config.passages.descriptions. |
config.disableHistoryControls |
Changed the config.disableHistoryControls property to Config.history.controls. This change also inverted the meaning of the property, so take note of that. |
config.disableHistoryTracking |
Replaced the config.disableHistoryTracking property with Config.history.maxStates. The new property works differently, so take note of that. |
config.displayPassageTitles |
Changed the config.displayPassageTitles property to Config.passages.displayTitles. |
config.historyMode |
Removed the config.historyMode property. It's unnecessary since there's now only one history mode in the engine. |
config.macros.disableIfAssignmentError |
Changed the config.macros.disableIfAssignmentError property to Config.macros.ifAssignmentError. This change also inverted the meaning of the property, so take note of that. |
config.passageTransitionOut |
Changed the config.passageTransitionOut property to Config.passages.transitionOut. Additionally, it no longer accepts a boolean value, which has been replaced by the name of the animating property (necessitated by changes to browser handling of transition animations). |
config.startPassage |
Changed the config.startPassage property to Config.passages.start. |
config.updatePageElements |
Changed the config.updatePageElements property to Config.ui.updateStoryElements. |
state)The History API object has been renamed to State and some of its methods have also changed. Furthermore, it is no longer instantiated into the legacy state object—which still exists, so legacy code will continue to work. See the State API docs for more information.
The State.display() method—formerly state.display()—is no longer overridable, meaning it cannot be wrapped—e.g. the "StoryRegions" modules do this. Instead, use Tasks.
Calling the State.initPRNG() method—formerly History.initPRNG()—outside of story initialization will now throw an error. It has always been required that the call happen during story initialization, the only change is the throwing of the error.
Math.random() is no longer replaced by the integrated seedable PRNG when State.initPRNG() is called. Instead, use either the built-in functions random() & randomFloat() or the State.random() method, if you need direct access to the PRNG—since it returns a call to either Math.random() or the seedable PRNG, as appropriate.
The Macros API object has been renamed to Macro and several of its methods have also changed, for better consistency with the other APIs. Furthermore, it is no longer instantiated into the legacy macros object—which still exists, so SugarCube-compatible legacy macros will continue to work. See the Macro API docs for more information.
| Macro | Changes |
|---|---|
<<back>> & <<return>> |
Replaced the ungainly link text syntax—<<back::linktext "…">>/<<return::linktext "…">>—with l10nStrings object properties—l10nStrings.macroBackText and l10nStrings.macroReturnText. |
<<if>> & <<elseif>> |
The <<if>> macro will now, optionally, return an error if the JavaScript = assignment operator is used (default: enabled). Configured via the Config.macros.ifAssignmentError config property. |
| Options macros | The various Options macros have been removed. See the Options system section for more information. |
The entire Options system—MenuOptions special passage, options special variable, and associated macros—has been scrapped for numerous reasons—it was always a hack, required copious amounts of boilerplate code to be useful, etc. It is replaced by the Setting API and settings special variable. See the Setting API docs for more information.
The SaveSystem API object has been renamed to Save and several of its methods have also changed, for better consistency with the other APIs. See the Save API docs for more information.
The UISystem API object has been split into two APIs Dialog and UI, and some of its methods have also changed. In particular, the parameter list for the Dialog.setup() method has changed. See the Dialog API and UI API docs for more information.
This is a reference for localizing SugarCube's default UI text, in general, and its l10nStrings object specifically.
NOTE: If you're simply looking to download ready-to-use localizations, see SugarCube's website (under Downloads > Localizations).
v2.0.0: Basic syntax.v2.10.0: Added l10nStrings object. Deprecated strings object.strings vs. l10nStringsPrior to SugarCube v2.10.0, the strings localization object was named strings. The new l10nStrings object has a simpler, flatter, set of properties and better support for replacement strings. Unfortunately, this means that the two objects are incompatible.
To ensure backwards compatibility of existing strings objects, if one exists within a project's scripts, the older object is mapped to the new l10nStrings object.
The capitalization and punctuation used within the default replacement strings is deliberate, especially within the error and warning strings. You would do well to keep your translations similar when possible.
Replacement patterns have the format {NAME} (e.g. {identity}), where NAME is the name of a property within either the l10nStrings object or, in a few cases, an object supplied locally where the string is used—these instances will be commented.
By convention, properties starting with an underscore (e.g. _warningIntroLacking) are used as templates, only being included within other localized strings. Feel free to add your own if that makes localization easier—e.g. for gender, plurals, and whatnot. As an example, the default replacement strings make use of this to handle various warning intros and outros.
In use, replacement patterns are replaced recursively, so replacement strings may contain patterns whose replacements contain other patterns. Because replacement is recursive, care must be taken to ensure infinite loops are not created—the system will detect an infinite loop and throw an error.
Properties on the strings localization object (l10nStrings) may be set within your project's script section (Twine 2: the Story JavaScript, Twine 1/Twee: a script-tagged passage) to override the defaults.
Due to the number of individual strings, and the length of some of them, it's recommended that you simply read the source code of the latest release version.
Source: l10n/strings.js @bitbucket.org
// Changing the project's reported identity to "story"
l10nStrings.identity = "story";
// Changing the text of all dialog OK buttons to "Eeyup"
l10nStrings.ok = "Eeyup";
// Localizing the title of the Restart dialog (n.b. machine translations, for example only)
l10nStrings.restartTitle = "Neustart"; // German (de)
l10nStrings.restartTitle = "Reiniciar"; // Spanish (es)