☀️🌙
<==> Click on ▶ on code samples in this documentation to preview the resulting Twine passage here! Also, click on the left border ← to view this preview with full window width.

Introduction

Report bugs and suggest features

If you think you've found a bug to report in Harlowe, want to make a feature suggestion, or wish to see what future features are already planned, simply visit the project's issues page.

I'd appreciate it if you could adhere to the following guidelines when reporting bugs or proposals:

File reports for this documentation, too!

Harlowe is maintained by one person (me) out of a single interactive fiction language development bedroom. As such, I only have a limited number of staff to keep watch over the documentation and the program. I appreciate all reports about documentation slip-ups, typos, outdated advice, flat-out incorrect examples, and so forth.

A brief note of thanks

As of July 2020, Harlowe's issues page and hosting is now at foss.heptapod.net. Big thanks to Octobus SAS for providing this free open-source software hosting service, and Clever Cloud SAS for that service's hosting.

Unstable 4.0 builds now available!

By popular demand: you can now access the latest in-progress build of Harlowe 4.0, for your own personal use as an installable story format in Twine. Be warned that Harlowe 4 has intentional incompatibility with Harlowe 3 code, Harlowe 3 CSS, and Harlowe 3 browser support, and that all use of these builds is entirely without warranty! Please scrutinise the change log to fully understand what changes Harlowe 4 brings.

Click here to get the unstable build (last updated 2025-05-27)

Unstable builds are updated at least within a week of all new developments.

What Harlowe does best

Harlowe is one of a small handful of story formats offered in Twine. Each has its own specific focus and audience. Harlowe is designed for the following kind of author and work.

No HTML, Javascript or CSS experience needed

At its core, Harlowe's language is designed to assist authors with no familiarity with HTML, Javascript or CSS. Rather than requiring knowledge of all three languages, Harlowe provides a single language that fulfills the basic needs of all three and whose parts seamlessly integrate.

Use layout syntax, such as columns and aligners, to provide structure and composition to your prose. Harlowe contains special values that represent one or more CSS styles bundled together for convenience. Attach them to single runs of prose to provide the equivalent of inline styles, or use variables or the (change:) or (enchant:) macros to globally affect certain labeled structures. Harlowe's coding language, inside macro calls, smooths over the pitfalls and discrepancies of Javascript - better type-checking and clearer syntax prevents obvious datatype bugs like 1 + "1", 1 == "1" or if (a = 1), more plentiful error messages replace silent failures and junk values like NaN or undefined, different data structures each use the same setting, getting and checking syntax, and the styling datatypes are easily handled alongside other data. Styles can be combined by simply adding them together, for instance, and computed based on the current game state.

Though, if you already have HTML, Javascript or CSS experience, and would prefer to leverage that experience as you create, you may wish to use SugarCube instead, which provides more direct access to the page's HTML elements and to the Javascript language.

Dynamic hypertext as a focus

I have a deep admiration for the storytelling potential of early and recent hypertext mediums, such as HyperCard, Shockwave, Flash, and early HTML fiction, and I have kept their versatility in mind when creating Harlowe. Harlowe heavily encourages you to think of a page as a dynamic interactive space, not just a sequence of prose followed by choices. Harlowe encourages you to place links and interactive elements in the midst of prose, not just at the end, and to use them to change the prose in surprising and unusual ways - inserting or removing text in a previously-read paragraph, changing the styling of words, changing just the link itself, and other such effects to reveal new meaning in the text and communicate your story in a manner unique to hypertext. If you would like to explore the storytelling potential of hypertext, it is my dear hope that you will find Harlowe satisfying.

Though, if you would prefer a more traditional, branching style of interactive prose writing, you may wish to use Chapbook or a non-Twine language like Ink instead.

Programming depth available when you need it

Don't let the preceding sections lead you to believe Harlowe is narrowly limited as a programming environment. Despite crafting Harlowe as a language apart from Javascript, I've nonetheless equipped it with tools and utilities to handle dense data manipulation. A wide collection of conversion macros and syntactic structures exist to convert and manipulate arrays, strings, and maps, including a lambda syntax, and the (macro:) macro lets you sculpt a personal sub-language within Harlowe. In addition, the Debug Mode gives you a live view of variables, styles, and game state as the story progresses. While these are not meant to be immediately useful to the first-time author, one who has grown more ambitious in their time with Harlowe may call upon them to make more computationally complicated stories, such as basic role-playing games. As you grow in programming confidence, Harlowe can follow you.

No specific simulation elements

Interactive fiction is commonly associated with text adventure games with a high degree of spatial simulation and procedurally generated text, where you control a player-character and manipulate objects and navigate rooms. Harlowe (and the other Twine story formats) is intended for a much wider variety of stories with a much lighter amount of interaction with the story's inner world, and as such it does not contain pre-built programming constructs for rooms, objects, inventories, manipulation verbs, and other common design affordances of text adventures. If you would prefer to write a story with a higher degree of simulation and interaction, you may wish to use a non-Twine language like Inform instead.

Three fundamentals of Harlowe

Harlowe is a markup language that you can apply to your Twine prose to make the prose interactive, non-linear, and game-like. This language's full contents of features may appear daunting, but all of its features revolve around the following three simple concepts, which, when understood, unlock understanding the rest of the language:

1. Macros produce either commands, or data values

A macro is the basic unit of code. It is either a command that changes the game's state or the prose in some way, such as the (set:) macro that saves data to a variable, or the (link-goto:) macro that puts a link inside the prose, or it produces a data value, such as a number or a styling instruction. All of Harlowe's macros are either one or the other.

(set: $companion to "Gallifrey") This is a command.
(print: $companion) This is a command.
(lowercase: "SCREECH") This is a value (a string).
(a: 3,4,5) This is a value (an array).
(for: each _num, 1,2,3) This is a value (a changer).

2. Attach values to hooks of text to change the text

To use macros to change your story's prose, you must place that prose between brackets, which make a hook. You can then attach one or more values to the front of the hook to change the prose. This syntactic structure is used for everything from text styling with (text-style:), to the narrative-branching of the (if:) macro, to more complex commands like (event:) or (for:).

(set: $ringStolen to false)
(if: $ringStolen)[The ring, as expected, is gone.]

In the above example, an "if" statement is created by attaching the (if:) changer value to the square-bracketed hook.

3. You can use anything that produces a value as if it was that value

(set: $text to "VALID")
(set: $slide to (transition:"slide-right"))

(lowercase: "VALID") This is valid.
(lowercase: "VAL" + "ID") This is valid.
(lowercase: "VAL" + (uppercase: "id")) This is valid.
(lowercase: $text) This is valid.

(transition:"slide-right")[VALID] This is valid.
$slide[VALID] This is valid.

It is important, when you read example code in this documentation, not to assume that you're restricted to very specific ways of writing macros and expressions. Just because an example uses a variable, doesn't mean you can't instead use a plain value, or a macro that produces that value, in that exact same place. Variables and macros are interchangeable with the values they contain or produce – a variable containing a number can be used anywhere that a number could be used, as can a macro that produces a number. This means you have a wide range of expressiveness in writing your story's code - you can save values into variables simply to save having to write them in full repeatedly, and you can use data-choosing macros like (either:), (cond:), (nth:) and so forth nearly anywhere you want. Use whatever form is most suited to making your prose readable!

Example stories

Here are a few example stories, written by me, Leon, and designed to be downloaded and opened in the Twine editor for reference and experimentation. These stories' prose and Harlowe code (though, of course, not the Harlowe engine itself) are entirely public domain - use their contents for your own projects as you wish.

Quack of Duckness [download].

This is a parody of the example story for the Chapbook story format, "Cloak of Darkness". This demonstrates several basic Harlowe features:

Additionally, it contains an extra passage with a "character creator" board, which allows players to spend statistic points to increase character attributes, in the manner of an RPG. This board makes use of these features:

The Basics of TBC [download].

This demonstrates how one would implement a very simple 1-vs-1 turn-based-combat (TBC) engine in Harlowe, in the manner of an RPG. It provides examples of the following features:

Styling with Enchantments [download].

This demonstrates a number of ways you can style your stories without needing to use CSS stylesheets. All of the styles in this story are coded in separate passages, and their code is free to use in your stories. They provide examples of the following features:

Enjoy the examples!

Editing and debugging

Editor toolbar

Harlowe is intended for use inside Twine 2, which installs with it included as the default format. When Harlowe is selected as the current story format for your story (via a dropdown menu in the Details tab of the Story menu), then it will augment Twine's passage editor with extra toolbar buttons. These buttons either allow markup to be quickly inserted into the passage code, or provide some other kind of assistance in editing. These buttons are outlined as follows.

Be aware for all of the above that, once OK is clicked and code has been placed, there isn't any way to "unmake" the code and return to the dialog that produced it. So, if you want to change some aspect of the resulting code, you'll have to edit it as-is.

The following buttons are toggleable - they enable or disable special editing modes or features.

The remaining buttons offer additional editing utilities.

Finally, while most of these buttons don't have visible labels, hovering over one will produce a small tooltip revealing its name.

Editor keyboard shortcuts

When using the passage editor in Twine 2, the following keyboard (and mouse) shortcuts are available. Note that many of these (in particular, those involving cursor movement and selections) are shared across all Twine story formats, not just Harlowe. In the notation below, substitute Ctrl for ⌘ if you are using MacOS.

Keyboard shortcut Purpose
Ctrl+Left Click Create additional text cursors. These cursors all output the same text at their location when you type, and can be moved simultaneously. Use this feature to edit or insert the same text in multiple locations.
Ctrl+Left Drag Create multiple, disconnected text selections without deselecting the current selections. These can all be edited simultaneously by typing, like single selections.
Ctrl+D Delete the entire line that the cursor is on.
Ctrl+Home Move the cursor to the top of the passage.
Ctrl+End Move the cursor to the bottom of the passage.
Ctrl+Left Move the cursor to the start of the previous "clump" of characters. These are either words (clumps of alphanumeric characters), runs of punctuation, or runs of multiple whitespace characters.
Ctrl+Right Move the cursor to the end of the next "clump" of characters. These are either words (clumps of alphanumeric characters), runs of punctuation, or runs of multiple whitespace characters.
Alt+Left Move the cursor to the start of the current line.
Alt+Right Move the cursor to the end of the current line.
Ctrl+B Wrap the current selected text in the "bold" style markup.
Ctrl+I Wrap the current selected text in the "italic" style markup.
Ctrl+- Wrap the current selected text in the "strikethrough" style markup.
Ctrl+. Wrap the current selected text in the "superscript" style markup.
Ctrl+F Open the Find/Replace panel.
Ctrl+G Go to the next Find/Replace match (scroll it onto the screen, and make it the target of the next "Replace" operation).
Ctrl+Shift+G Go to the previous Find/Replace match.
Ctrl+H Replace the current Find/Replace match.

In addition to these, the most basic text editor operations (like using Ctrl-Z to undo, Ctrl-Y or Ctrl-Shift-Z to redo, Ctrl+A to select all of the text, Shift+Left or Shift+Right to adjust the current selection, dragging the current selected text to reposition it, and the like) are, of course, also available.

Debug Mode

If you select "Test" or use the "Test From Here" feature in the Twine editor, or use the (debug:) macro, Harlowe will enter Debug Mode. Debug Mode provides a number of useful features for testing your story, examining how it runs, and checking what variables contain.

The exact differences between normal play and Debug Mode are as follows.

Generally speaking, Debug Mode is not intended to be used by your story's players, and you aren't expected to distribute stories with Debug Mode enabled.

Debug Mode panel:

The Debug Mode panel sits on the lower-right corner of the browser window. It provides access to the following features.

Debug View:

This is a special viewing mode that makes otherwise-invisible code structures visible, and lets you examine how they were interpreted by Harlowe. When enabled, it applies the following features.

Replays:

Clicking the 🔍 buttons on macro calls or error messages will produce a replay of that call, which is a dialog showing a step-by-step view of how the macro call's code was interpreted by Harlowe, starting with the initial call as written (such as (if: 2 + 5 > (max: 3, 8))) and computing that call's values one step at a time (in this case, the steps are (if: 7 > (max: 3, 8)), (if: 7 > 8) and (if: false)).

For performance reasons, Harlowe only records replay data after Debug Mode is enabled (and if the setting to record replays is enabled in Debug Mode's settings). The (debug:) macro can enable it after startup, but any macro calls that were run after the (debug:) call was run will have replay data available.

Passage markup

Hyperlinks are the player's means of moving between passages and affecting the story. They consist of link text, which the player clicks on, and a passage name to send the player to.

Inside matching non-nesting pairs of [[ and ]], place the link text and the passage name, separated by either -> or <-, with the arrow pointing to the passage name.

You can also write a shorthand form, where there is no <- or -> separator. The entire content is treated as a passage name, and its evaluation is treated as the link text.

Example usage:

[[Go to the cellar->Cellar]] is a link that goes to a passage named "Cellar".
[[Parachuting<-Jump]] is a link that goes to a passage named "Parachuting".
[[Down the hatch]] is a link that goes to a passage named "Down the hatch".

Details:

The interior of a link (the text between [[ and ]]) may contain any character except ]. If additional ->s or <-s appear, the rightmost right arrow or leftmost left arrow is regarded as the canonical separator.

If the passage name of a link does not exactly match that of an existing passage, but it does if you render the markup in or around it, then Harlowe will use that name. So, you can put markup inside the link, as well as variables or value macros like (either:).

However, you can't put commands or changers in the passage name. [[Really, now.->(print:$explain)]] will cause an error.

Links can be customised by attaching changer macros, like (transition-depart:) or (text-style:). Just place one in front of the link, like so: (t8n-depart:"dissolve")[[Recall that day]] - or attach a variable containing one: $memory[[Recall that day]]. You can also customise every link in the passage using (change:) or (enchant:), and ?Link.

This syntax is not the only way to create links – there are many link macros, such as (link:), which can be used to make more versatile hyperlinks in your story.

Style markup

It's expected that you'd want to apply styles to your text – to italicise a word in dialogue, for example. You can do this with simple formatting codes that are similar to the double brackets of a link. Here is what's available to you:

Styling Markup code Result HTML produced
Italics //text// text <i>text</i>
Boldface ''text'' text <b>text</b>
Strikethrough text ~~text~~ text <s>text</s>
Emphasis *text* text <em>text</em>
Strong emphasis **text** text <strong>text</strong>
Superscript meters/second^^2^^ meters/second2 meters/second<sup>2</sup>

Emphasis and strong emphasis appear identical to italics and boldface by default (though they can be changed using CSS) and are offered for those with familiarity with the Markdown language. Italics and boldface are offered for those with familiarity with SugarCube, Twine 1, or TiddlyWiki.

The alternative Markdown emphasis syntax _text_ and __text__ is not available. Harlowe reserves the use of the _ character for temp variables.

Example usage:

You //can't// be serious! I have to go through the ''whole game''
again? ^^Jeez, Louise!^^

Details:

You can nest these codes - ''//text//'' will produce bold italics - but they must nest symmetrically. ''//text''// will not work.

A larger variety of text styles can be produced by using the (text-style:) macro, attaching it to a text hook you'd like to style. And, furthermore, you can use HTML tags like <mark> as an additional styling option.

Macro markup

A macro is a piece of code that is inserted into passage text. Macros are used to accomplish many effects, such as altering the game's state, displaying different text depending on the game's state, and altering the manner in which text is displayed.

Built in macros:

There are many built-in macros in Harlowe. To use one, you must call upon it in your passage by writing the name, a colon, and some data values to provide it, all in parentheses. For instance, you call the (print:) macro like so: (print: 54). In this example, print is the macro's name, and 54 is the value.

The name of the macro is case-insensitive, dash-insensitive and underscore-insensitive. This means that almost any combination of case, dashes and underscores in the name will be ignored. You can, for instance, write (go-to:) as (goto:), (Goto:), (GOTO:), (GoTo:), (Go_To:), (Got--o:), (-_-_g-o-t-o:), or almost any other combination or variation. There is, however, ONE exception: the name cannot start with an underscore _, because that would make it a temp variable.

Custom macros:

In addition to built-in macros, it is also possible to write your own macros, using the (macro:) macro. You need to save these macros inside a variable or temp variable using the (set:) macro. Once you've done so, you can call it much like it was a built-in macro, except by replacing the name with the variable: ($someCustomMacro:) is how you would call a custom macro stored in the variable $someCustomMacro, and (_anotherCustomMacro:) is how you would call a custom macro stored in the temp variable _anotherCustomMacro. Note that you can't use dataname access to use macros that are inside arrays or datamaps: ($array's 1st:) is, unfortunately, not a valid custom macro call.

Passing data:

You can provide any type of data values to a macro call - numbers, strings, booleans, and so forth. These can be in any form, as well - "Red" + "belly" is an expression that produces a single string, "Redbelly", and can be used anywhere that the joined string can be used. Variables, too, can be used with macros, if their contents matches what the macro expects. So, if $var contains the string "Redbelly", then (print: $var), (print: "Redbelly") and (print: "Red" + "belly") are exactly the same.

Furthermore, each macro call produces a value itself - (num:), for instance, produces a number, (a:) an array - so they too can be nested inside other macro calls. (if: (num:"5") > 2) nests the (num:) macro inside the (if:) macro.

If a macro can or should be given multiple values, separate them with commas. You can give the (a:) macro three numbers like so: (a: 2, 3, 4). The final value may have a comma after it, or it may not - (a: 2, 3, 4,) is equally valid. Also, if you have a data value that's an array, string or dataset, you can "spread out" all of its values into the macro call by using the ... operator: (either: ...$array) will act as if every value in $array was placed in the (either:) macro call separately

Historical note:

You might notice that the majority of Harlowe macros are not, strictly speaking, macros in the computer-science sense, but are more like functions. This is purely due to historical circumstance - the original Twine 1 story format, Jonah, was based on TiddlyWiki's engine, which features parameterised transclusions called "macros". These are closer to computer-science macros in that they actually transclude markup directly into the tiddler (TiddlyWiki's term for "passage"). Thus, only Harlowe command macros like (display:) can really be considered "proper" macros.

Variable markup

As described in the documentation for the (set:) macro, variables are used to remember data values in your game, keep track of the player's status, and so forth. They start with $ (for normal variables) or _ (for temp variables, which only exist inside a single passage, hook or lambda).

Due to this syntax potentially conflicting with dollar values (such as $1.50) in your story text, variables cannot begin with a numeral.

You can print the contents of variables, or any further items within them, using the (print:) and (for:) macros. Or, if you only want to print a single variable, you can just enter the variable's name directly in your passage's prose.

(set: $plushieName to "Whispy", _heldItem to "briefcase")
Your beloved plushie, $plushieName, awaits you after a long work day.
You put your _heldItem down and lift it for a snuggle.

Furthermore, if the variable contains a changer command, such as that created by (text-style:) and such, then the variable can be attached to a hook to apply the changer to the hook:

(set: $robotText to (font:"Courier New"), _assistantText to (size:0.8))
$robotText[Good golly! Your flesh... it's so soft!]
_assistantText[Don't touch me, please! I'm ticklish.]

Note: While you can normally display the contents of variables by simply placing their names directly in passage prose, such as $ship or $crew, you have to use another macro, such as (print:), to display the contents of arrays, datamaps, or other structures, such as (print: $ship's mast) or (print: $crew's 1st).

Note 2: Even though named hooks' names are case-insensitive, variable names are case-sensitive. So, $Chips and $chips are considered different variables.

Note 3: In Harlowe 3, If you use a story-wide variable that doesn't exist (that is, it hasn't been created via (set:), (put:), and so forth), then a default value of 0 will be used in its place. So, (print: $nonexistantVariable) will show the text "0". This is likely to change in a future version of Harlowe, however.

Hook markup

A hook is a means of indicating that a specific span of passage prose is special in some way. It essentially consists of text between single [ and ] marks. Prose inside a hook can be modified, styled, controlled and analysed in a variety of ways using macros.

A hook by itself, such as [some text], is not very interesting. However, if you attach a macro or a variable to the front, the attached value is used to change the hook in some way, such as hiding it based on the game state, altering the styling of its text, moving its text to elsewhere in the passage.

(font: "Courier New")[This is a hook.

As you can see, this has a macro call in front of it.]
This text is outside the hook.

The (font:) macro is one of several macros which produces a special styling changer, instead of a basic data type like a number or a string. In this case, the changer changes the attached hook's font to Courier New, without modifying the other text.

You can save this changer to a variable, and then use it repeatedly, like so.

(set: $x to (font: "Tahoma"))
$x[This text is in Tahoma.]
$x[As is this text.]

The basic (if:) macro is used by attaching it to a hook, too.

(if: $x is 2)[This text is only displayed if $x is 2.]

For more information about changer macros, consult the descriptions for each of them in turn.

Named hook markup

For a general introduction to hooks, see their respective markup description. Named hooks are a less common type of hook that offer unique benefits. To produce one, attach a "nametag" to the front or back, similar to how a macro call would be attached:

[This hook is named 'opener']<opener|

|s2>[This hook is named 's2']

(Hook nametags are supposed to resemble triangular gift box nametags.)

A macro can refer to and alter the text content of a named hook by referring to the hook as if it were a variable. To do this, write the hook's name as if it were a variable, but use the ? symbol in place of the $ symbol:

[Fie and fuggaboo!]<shout|

(click: ?shout)[ (replace: ?shout)["Blast and damnation!"] ]

The above (click:) and (replace:) macros can remotely refer to and alter the hook using its name. This lets you, for instance, write a section of text full of tiny hooks, and then attach behaviour to them further in the passage:

Your [ballroom gown]<c1| is [bright red]<c2| with [silver streaks]<c3|,
and covered in [moonstones]<c4|.

[]<c5|
(click: ?c1)[(replace:?c5)[A hand-me-down from your great aunt.]]
(click: ?c2)[(replace:?c5)[A garish shade, to your reckoning.]]
(click: ?c3)[(replace:?c5)[Only their faint shine keeps them from being seen as grey.]]
(click: ?c4)[(replace:?c5)[Dreadfully heavy, they weigh you down and make dancing arduous.]]

As you can see, the top sentence remains mostly readable despite the fact that several words have (click:) behaviours assigned to them.

Built in names:

There are four special built-in hook names, ?Page, ?Passage, ?Sidebar, and ?Link, which, in addition to selecting named hooks, also affect parts of the page that you can't normally style with macros. They can be styled using the (change:) or (enchant:) macros.

(Note that, as mentioned above, if you use these names for your own hooks, such as by creating a named hook like |passage>[], then they will, of course, be included in the selections of these names.)

Hidden hook markup

Hidden hooks are an advanced kind of named hook that can be shown using macros like (show:). For a general introduction to named hooks, see their respective markup description.

There may be hooks whose contained prose you don't want to be visible as soon as the passage appears - a time delay, or the click of a link should be used to show them. You can set a hook to be hidden by altering the hook tag syntax - replace the > or < mark with a parenthesis.

|visible>[This hook is visible when the passage loads.]
|cloaked)[This hook is hidden when the passage loads, and needs a macro like `(show:?cloaked)` to reveal it.]

[My commanding officer - a war hero, and a charismatic face for the military.]<sight|
[Privately, I despise the man. His vacuous boosterism makes a mockery of my sacrifices.](thoughts|

(You can think of this as being visually similar to the pointed tails of comic speech balloons vs. round, enclosed thought balloons.)

In order to be useful, hidden hooks must have a name, which macros like (show:) can use to show them. Hence, there's no way to make a hidden unnamed hook - at least, without using a conditional macro like (if:).

Unclosed hook markup

This is a special version of the hook markup - an open bracket [, followed by any number of = marks, that has no matching closing bracket. When it is placed in a passage, it indicates that all the prose that follows, until the end of the hook that contains it or the end of the passage, is part of a single hook.

Its main purpose is to let you easily deploy hook changers that apply to the remaining text of the passage, without having to place and keep track of closing brackets at the end. For instance, the (click:) macro can be used with the ?page hook name to prompt the reader to click anywhere on the page to reveal the rest of the passage. The unclosed hook markup lets you use it as many times as you want, without needing to balance a number of closing brackets at the end of the passage.

(click: ?page)[==
This text won't appear until the page is clicked once.
(click: ?page)[==
This text won't appear until the page is clicked twice.
(click: ?page)[==
This text won't appear until the page is clicked three times.

Other changer macros, such as (link:), (more:), (event:), and (transition:), also work well with this markup.

Also, unclosed hooks can be named, and marked as hidden, just like other hooks.

|1>[=
The rest of this passage is in a hook named "1".
|2)[=
This part is also in a hidden hook named "2".

HTML markup

If you are familiar with them, HTML tags (like <img>) and HTML escapes (like &sect;) can be inserted straight into your passage text. They are treated very naively - they essentially pass through Harlowe's markup-to-HTML conversion process untouched.

Example usage:

<mark>This is marked text.

&para; So is this.

And this.</mark>

Details:

HTML elements included in this manner are given a data-raw attribute by Harlowe, to distinguish them from elements created via markup.

You can include a <script> tag in your passage to run Javascript code. The code will run as soon as the containing passage code is rendered. See the "HTML script tag" article for more details.

You can also include a <style> tag containing CSS code. The CSS should affect the entire page until the element is removed from the DOM. You could use this in a "header" or "footer" tagged passage, inside an (if:) hook, to make the CSS apply to every passage where the (if:) condition is fulfilled.

Finally, you can also include HTML comments <!-- Comment --> in your code, if you wish to leave reminder messages or explanations about the passage's code to yourself.

HTML script tag markup

This section details further information about the workings of <script> elements placed inside Harlowe passages, and how the Javascript code relates to the Harlowe code in the containing passage.

If a <script> tag has a type attribute, and the MIME-type in that attribute is anything other than 'text/javascript', Harlowe will ignore it (in keeping with HTML's normal behaviour).

No Harlowe internal methods and modules are currently accessible inside scripts. However, jQuery 3.6 may be accessed from the $ global variable. This is a third-party library that Harlowe uses internally and whose code is included in every compiled story, and is not retrieved via CDN or any online connections.

As of Harlowe 3.3.0, <script> elements are run while Harlowe runs the macros and expressions in a passage. Thus, if a macro or hook is before the <script> tag in the passage, it will be run beforehand, and if a macro or hook is after it, it will be run after it. To defer the execution of Javascript code until the passage is fully rendered or run, consider using a setTimeout callback.

As of Harlowe 3.3.0, Javascript code in <script> elements (that is, without a src attribute) has access to Harlowe variables - both story-wide variables that begin with $, and temporary (temp) variables that begin with _ which are visible in the same hook as the <script> element. Harlowe variables are accessed by writing the Harlowe variable name as if it were a Javascript variable. Valid Harlowe variable names are also coincidentally valid Javascript names, so there's no need to escape or modify them. Assigning to these names will immediately update the Harlowe variable, if possible.

Example usage:

(set: _harloweVariable to "You're reading the ")\
<script>
_harloweVariable += document.title.bold();
</script>\
(print: _harloweVariable)

Details:

To eliminate any confusion: These names are not Javascript variables, or even global window properties, but object getters and setters added to the script's scope using a with statement. These names do not pollute or overwrite the global scope (although they shadow any global variables with the same names, such as window.$arr being shadowed by a Harlowe variable $arr), and remain accessible and current even inside a callback created within the script. Moreover, even though these names are created using a with statement, it is still possible to opt into Javascript's "strict mode" by placing the "use strict" pragma at the start of the script.

If "strict mode" is not enabled for the script, an error will not occur if you attempt to assign to a Harlowe variable that doesn't exist, such as by writing $arr = [] - instead, a global Javascript variable will be created.

Harlowe variables in Javascript currently have the following restrictions.

Finally, there is no way to call Harlowe macros from Javascript, at present. There is also no way to access identifiers like exits or visits from Javascript, at present.

A redundant note:

Harlowe is not meant to be a story format that you write using Javascript instead of its own language, and I have taken great care in designing it such that authors are not rewarded for knowing how to write Javascript. The <script> element feature is intended solely to complement existing Harlowe code, by allowing small samples of Javascript to minimally interact with it. As such, I recommend using it sparingly and judiciously. That being said, feel free to use this feature in any way you wish, as long as you understand whether your usage lines up with its purpose and intent.

Verbatim markup

As plenty of symbols have special uses in Harlowe, you may wonder how you can use them normally, as mere symbols, without invoking their special functionality. You can do this by placing them between a pair of ` marks.

If you want to escape a section of text which already contains single ` marks, simply increase the number of ` marks used to enclose them.

Example usage:

There's no hard limit to the amount of graves you can use to enclose the text.

If you want to make an entire hook to be displayed verbatim, without its markup being rendered, you can attach the (verbatim:) changer.

Bulleted list markup

You can create bullet-point lists in your text by beginning lines with an asterisk *, followed by whitespace, followed by the list item text. The asterisk will be replaced with an indented bullet-point. Consecutive lines of bullet-point items will be joined into a single list, with appropriate vertical spacing.

Remember that there must be whitespace between the asterisk and the list item text! Otherwise, this markup will conflict with the emphasis markup.

If you use multiple asterisks (**, *** etc.) for the bullet, you will make a nested list, which is indented deeper than a normal list. Use nested lists for "children" of normal list items.

Example usage:

 * Bulleted item
    *    Bulleted item 2
  ** Indented bulleted item

Numbered list markup

You can create numbered lists in your text, which are similar to bulleted lists, but feature numbers in place of bullets. Simply begin single lines with 0., followed by whitespace, followed by the list item text. Consecutive items will be joined into a single list, with appropriate vertical spacing. Each of the 0.s will be replaced with a number corresponding to the item's position in the list.

Remember that there must be whitespace between the 0. and the list item text! Otherwise, it will be regarded as a plain number.

If you use multiple 0. tokens (0.0., 0.0.0. etc.) for the bullet, you will make a nested list, which uses different numbering from outer lists, and are indented deeper. Use nested lists for "children" of normal list items.

Example usage:

0. Numbered item
   0. Numbered item 2
 0.0. Indented numbered item

Aligner markup

An aligner is a special single-line token which specifies the alignment of the subsequent text. It is essentially 'modal' - all text from the token onward (until another aligner is encountered) is wrapped in a <tw-align> element (or unwrapped in the case of left-alignment, as that is the default).

Any amount of whitespace is permitted before or after each token, as long as it is on a single line.

Example usage:

==>
This is right-aligned
  =><=
This is centered
 <==>
This is justified
<==
This is left-aligned (undoes the above)
===><=
This has margins 3/4 left, 1/4 right
  =><=====
This has margins 1/6 left, 5/6 right.

(Try expanding this code preview using the bar on the left.)

You may apply alignment to specific hooks in your passages by attaching the (align:) macro to them.

Column markup

Column markup is, like aligner markup, a special single-line token which indicates that the subsequent text should be laid out in columns. They consist of a number of | marks, indicating the size of the column relative to the other columns - the total width of all columns equals the page width, and this is divided among the columns by their | marks. They also have a number of = marks surrounding it, indicating the size of the column's margins in CSS "em" units (which are about the width of a capital M).

All text from the token onward, until the next token is encountered, is contained in the specified column. A |==| token ends the set of columns and returns the page to normal.

Columns are laid out from left to right, in order of appearance.

Any amount of whitespace is permitted before or after each token, as long as it is on a single line.

Example usage:

(change:?passage, (text-size:0.6))
|==
This is in the leftmost column, which has a right margin of about 2 letters wide.
    =|||=
This is in the next column, which has margins of 1 letter wide. It is three times as wide as the left column.
 =====||
This is in the right column, which has a right margin of about 5 letters wide. It is twice as wide as the left column.
  |==|
This text is not in columns, but takes up the entire width, as usual.

(Try expanding this code preview using the bar on the left.)

You can create nested columns by enclosing the inner set of columns in an unnamed hook, like so.

(change:?passage, (text-size:0.6))
|==
This is the outer left column.
==|
This is the outer right column.
[\
  |==
This is the inner left column, inside the outer right column.
  ==|
This is the inner right column, inside the outer right column.
\]

Heading markup

Heading markup is used to create large headings, such as in structured prose or title splash passages. It is almost the same as the Markdown heading syntax: it starts on a fresh line, has one to six consecutive #s, and ends at the line break.

Example usage:

#Level 1 heading renders as an enclosing `<h1>`
   ###Level 3 heading renders as an enclosing `<h3>`
 ######Level 6 heading renders as an enclosing `<h6>`

As you can see, unlike in Markdown, opening whitespace is permitted before the first #.

Horizontal rule markup

A hr (horizontal rule) is a thin horizontal line across the entire passage. In HTML, it is a <hr> element. In Harlowe, it is an entire line consisting of 3 or more consecutive hyphens -.

Example usage:

        ---
  ----
     -----

Again, opening whitespace is permitted prior to the first - and after the final -.

Whitespace markup

"Whitespace" is a term that refers to "space" characters that you use to separate programming code tokens, such as the spacebar space, and the tab character. When used inside the macro syntax, they are considered interchangeable in type and quantity - using two spaces usually has the same effect as using one space, one tab, and so forth.

Harlowe tries to also recognise most forms of Unicode-defined whitespace, including the quads, the per-em and per-en spaces, but not the zero-width space characters (as they may cause confusion and syntax errors if unnoticed in your code).

Collapsing whitespace markup

When working with macros, HTML tags and such, it's convenient for readability purposes to space and indent the text. However, this whitespace will also appear in the compiled passage text. You can get around this by placing the text between { and } marks. Inside, all runs of consecutive whitespace (line breaks, spaces) will be reduced to just one space.

Example usage:

{
    This sentence
    will be
    (set: $event to true)
    written on one line
    with only single spaces.
}

Details:

If you wish to still have line breaks within the markup that won't be collapsed, you can use HTML <br> tags (see the HTML markup section for more information about raw HTML tags).

You can nest this markup within itself - {Good { gumballs!}} - but the inner pair won't behave any differently as a result of being nested.

Text inside macro calls (in particular, text inside strings provided to macro) will not be collapsed. Neither will text outputted by macro calls, either - {(print:"\n\n\n")} will still print 3 newlines (see the String article for the meaning of the \n escape code), and {(display:"Attic")} will still display all of the whitespace in the "Attic" passage.

Also, newlines inside the verbatim syntax will not be collapsed either.

{Thunder`
`hound}

Note that Harlowe's default CSS already collapses consecutive spaces in a single line, but not vertical whitespace (which is converted to <br> elements). If you change it, using the white-space CSS property (such as by white-space:break-spaces in your story stylesheet), then the effects of this syntax in removing horizontal whitespace will become noticeable.

If the markup contains a (replace:) command attached to a hook, the hook will still have its whitespace collapsed, even if it is commanded to replace text outside of the markup.

You may apply this collapsing effect to specific hooks using the (collapse:) macro. In particular, if you wish for the entire passage's whitespace to collapse, consider using (change: ?passage) and (collapse:).

If you only want to remove specific line breaks, consider the escaped line break markup.

Unclosed collapsing whitespace markup

This is a special version of the collapsing whitespace markup - an open curly brace {, followed by any number of = marks, that has no matching closing brace. When it is placed in a passage, it indicates that all the prose that follows, until the end of the hook that contains it or the end of the passage, should have its whitespace collapsed.

As with the the unclosed hook markup, this has advantages in situations where keeping track of closing brackets would be slightly inconvenient. If you use revision macros or enchantment macros like (change:), (replace:), (click:) and so forth, you can place those at the end of your passage, and use a single {= to separate them from the rest of the passage. Additionally, you can place a {= at the start of your passage to cause the entire passage's whitespace to be collapsed, allowing you to write additional prose without needing to have a closing brace after all of your additions.

This part of the passage
has normal whitespace.
{=
This part of the passage
has collapsed
whitespace.

All of the details pertaining to the collapsing markup apply here - consult its article for more information.

Escaped line break markup

Sometimes, you may want to write an especially long line, potentially containing many macros. This may not be particularly readable in the passage editor, though. One piece of markup that may help you is the \ mark - placing it just before a line break, or just after it, will cause the line break to be removed from the passage, thus "joining together" the lines.

Example usage:

This line \
and this line
\ and this line, are actually just one line.

Details:

There must not be any whitespace between the \ and the line break. Otherwise, it won't work. (In the Twine editor, the \ will change from yellow to gray, indicating it's no longer considered an escaped line break.)

Like most passage text markup, this cannot be used inside a macro call (for instance, (print: \
3)) - but since line breaks between values in macro calls are ignored, this doesn't matter.

List of macros

The (set: ) macro

(set: ...VariableToValue) Instant

Stores data values in variables, optionally allowing you to permanently restrict the variable to a single datatype.

Example usage:

Rationale:

Variables are data storage for your game. You can store data values under special names of your choosing, and refer to them later.

There are two kinds of variables. Normal variables, whose names begin with $, persist between passages, and should be used to store data that will be needed throughout the entire game. Temp variables, whose names begin with _, only exist inside the hook or passage that they're first (set:), and are forgotten after the hook or passage ends. You should use temp variables if you're writing passage code that mustn't accidentally affect any other passages' variables (by using (set:) on a variable name that someone else was using for something different). This can be essential in collaborative work with other authors working on the same story independently, or when writing code to be used in multiple stories.

The following example demonstrates where temp variables are usable.

(set: _a to 1) <- This is usable everywhere in this passage.
[
    (set: _b to 1) <-- This is only usable inside this hook.
    (set: _a to it + 1) <-- This changes the outer _a variable.
    [
        (print: _a + _b) <-- You can refer to both _a or _b in this hook.
    ]
]
(print: _b) <-- This will cause an error.

Variables have many purposes: keeping track of what the player has accomplished, managing some other state of the story, storing hook styles and changers, and other such things. You can display variables by putting them in passage text, attach them to hooks, and create and change them using the (set:) and (put:) macros.

Details:

Even though named hooks' names are case-insensitive, variable names are case-sensitive. So, $Chips and $chips are considered different variables.

In its basic form, a variable is created or changed using (set: variable to value ). You can also set multiple variables in a single (set:) by separating each VariableToValue with commas: (set: $weapon to 'hands', $armour to 'naked'), etc. Note: currently, each value in a VariableToValue is evaluated before any of them are stored in their variables. This means that, for instance, (set: $olives to 5)(set: $olives to 2, $grapes to $olives - 1) will, in the second (set:) call, cause 2 and $olives - 1 to be evaluted to 2 and 5 - 1 (i.e. 4) before being put in $olives and $grapes, respectively. This may change in a future version of Harlowe.

You can also use it in expressions on the right-side of to. Much as in other expressions, it's a shorthand for what's on the left side: (set: $vases to it + 1) is a shorthand for (set: $vases to $vases + 1).

Due to the variable syntax potentially conflicting with dollar values (such as $1.50) in your story text, variables cannot begin with a numeral.

Typed variables:

A common source of errors in a story is when a variable holding one type of data is mistakenly overridden with a different type of data, such as when putting "1" (the string "1") into a variable that should hold numbers. A good way to guard against this is to make the variable a typed variable, which is permanently restricted to a single datatype. The first time you set data to the variable, write (set: num-type $days to 1) to permanently restrict $days to numbers. That way, if you accidentally put "1" into it, an error will appear immediately, explaining the issue. Moreover, typed variables serve a code documentation purpose: they help indicate and explain the purpose of a variable by showing what data is meant to be in it. You can use any datatype before the -type syntax - see the article about datatype data for more details.

In addition to just restricting a variable to a type, you may wish to specify that a variable should only hold one value for the entire story - a style changer, for instance, or a datamap holding fixed values for a procedural-generation algorithm. For these, you want to use the const (short for "constant") datatype. Using this, the variable is guaranteed to constantly hold that value for the entirety of the story (or, if it's a temp variable, the passage or hook).

See also:

(put:), (move:), (unpack:)

The (put: ) macro

(put: ...VariableToValue) Instant

A left-to-right version of (set:) that requires the word into rather than to.

Example usage:

Rationale:

This macro has an identical purpose to (set:) - it creates and changes variables. For a basic explanation, see the rationale for (set:).

Almost every programming language has a (set:) construct, and most of these place the variable on the left-hand-side. However, a minority, such as HyperTalk, place the variable on the right. Harlowe allows both to be used, depending on personal preference. (set:) reads as (set: variable to value ), and (put:) reads as (put: value into variable ).

Details:

Just as with (set:), a variable is changed using (put: value into variable ). You can also set multiple variables in a single (put:) by separating each VariableToValue with commas: (put: 2 into $batteries, 4 into $bottles), etc.

You can also use typed variables with (put:) - (put: 1 into num-type $days) permanently restricts $days to numbers. Consult the article about (set:) for more information about typed variables.

it can also be used with (put:), but, interestingly, it's used on the right-hand side of the expression: (put: $eggs + 2 into it).

See also:

(set:), (move:), (unpack:)

The (move: ) macro

(move: ...VariableToValue) Instant

A variant of (put:) that, if transferring data from a data structure, deletes the source value after copying it - in effect moving the value from the source to the destination.

Example usage:

Rationale:

You'll often use data structures such as arrays or datamaps as storage for values that you'll only use once, such as a list of names to print out. When it comes time to use them, you can remove it from the structure and retrieve it in one go.

Details:

You must use the into keyword, like (put:), with this macro. This is because, like (put:), the destination of the value is on the right, whereas the source is on the left.

As with (set:) and (put:), you can also change multiple variables in a single (move:) by separating each VariableToValue with commas: (move: $a's 1st into $b, $a's 2nd into $c), etc. Also, unpacking syntax (described in detail in (unpack:)'s article) can be used with (move:) as well - (move: $array into (a: $x, $y)) will cause only the first and second values of $array to be moved into $x and $y.

If the data value you're accessing cannot be removed - for instance, if it's an array's length - then an error will be produced.

This macro works very well with the random data value of arrays: (move: $deck's random into $card) will remove a random value from $deck and put it into $card. Thus, you can use arrays as random "decks" of values that you can draw from and use once in your story.

Note that this will only delete the data from the source if the source is inside a data structure. Moving from variable to variable, such as by (move:$p into $q), won't cause $p to be deleted.

Just as with (set:) or (put:), typed variables can also be used with the destination variable of (move:). Writing (move: $enemies's 1st into dm-type $currentEnemy) will move a datamap from $enemies's 1st and put it into $currentEnemy, while also restricting $currentEnemy to datamap data for the rest of the story. Note that if $enemies's 1st is not, in fact, a datamap, an error will result.

See also:

(put:), (set:)

The (print: ) macro

(print: Any) Command

This command prints out any data provided to it, as text.

Example usage:

(print: $var + "s")

Details:

It is capable of printing things which (str:) cannot convert to a string, such as changers - but these will usually become bare descriptive text like [A (font: ) command]. You may find this useful for debugging purposes.

This command can be stored in a variable instead of being performed immediately. Notably, the expression to print is stored inside the command, instead of being re-evaluated when it is finally performed. So, a passage that contains:

(set: $name to "Dracula")
(set: $p to (print: "Count " + $name))
(set: $name to "Alucard")
$p

will still result in the text Count Dracula. This is not particularly useful compared to just setting $p to a string, but is available nonetheless.

Note that, once stored in a variable, a (print:) command is not a string. So, you can't provide it to (upperfirst:) and other such macros. (upperfirst: (print: $name)) will produce an error. However, if $name contains a string, you can provide it to (upperfirst:) before giving it to (print:), such as (print: (upperfirst: $name)).

If you need this command to print strings without the markup in the string being rendered, you may use the (verbatim:) changer to change the command, or use the (verbatim-print:) variant instead.

See also:

(str:), (display:), (verbatim-print:)

The (display: ) macro

(display: String) Command

This command writes out the contents of the passage with the given string name. If a passage of that name does not exist, this produces an error.

Example usage:

(display: "Cellar") prints the contents of the passage named "Cellar".

Rationale:

Suppose you have a section of code or source that you need to include in several different passages. It could be a status display, or a few lines of descriptive text. Instead of manually copy-pasting it into each passage, consider placing it all by itself in another passage, and using (display:) to place it in every passage. This gives you a lot of flexibility: you can, for instance, change the code throughout the story by just editing the displayed passage.

Details:

Text-targeting macros (such as (replace:)) inside the displayed passage will affect the text and hooks in the outer passage that occur earlier than the (display:) command. For instance, if passage A contains (replace:"Prince")[Frog], then another passage containing Princes(display:'A') will result in the text Frogs.

Like all commands, this can be set into a variable. It's not particularly useful in that state, but you can use that variable in place of that command, such as writing $var in place of (display: "Yggdrasil").

The (if: ) macro

(if: Boolean) Changer

This macro accepts only booleans (variables with true or false, or expressions using is, contains, or another such operator), and produces a changer that can be attached to hooks to hide them "if" the value was false.

Example usage:

(if: $legs is 8)[You're a spider!] will show the You're a spider! hook if $legs is 8. Otherwise, it is not run.

Rationale:

In a story with multiple paths or threads, where certain events could occur or not occur, it's common to want to run a slightly modified version of a passage reflecting the current state of the world. The (if:), (unless:), (else-if:) and (else:) macros let these modifications be switched on or off depending on variables, comparisons or calculations of your choosing.

Details:

Note that the (if:) macro only runs once, when the passage or hook containing it is rendered. Any future change to the condition (such as a (link:) containing a (set:) that changes a variable) won't cause it to "re-run", and show/hide the hook anew.

However, if you attach (if:) to a named hook, and the (if:) hides the hook, you can manually reveal the hook later in the passage (such as, after a (link:) has been clicked) by using the (show:) macro to target the hook. Named hooks hidden with (if:) are thus equivalent to hidden named hooks like |this)[].

Alternatives:

The (if:) and (hidden:) macros are not the only attachment that can hide or show hooks! In fact, a variable that contains a boolean can be used in its place. For example:

(set: $foundWand to true, $foundHat to true, $foundBeard to true)
(set: $isAWizard to $foundWand and $foundHat and $foundBeard)

$isAWizard[You wring out your beard with a quick twisting spell.]
You step into the ruined library.
$isAWizard[The familiar scent of stale parchment comforts you.]

By storing a boolean inside $isAWizard, it can be used repeatedly throughout the story to hide or show hooks as you please.

if you want to conditionally display very short strings, or small values inside a macro call, you may want to use the shorter (cond:) macro instead.

See also:

(unless:), (else-if:), (else:), (cond:), (show:)

The (unless: ) macro

(unless: Boolean) Changer

This macro is the negated form of (if:): it accepts only booleans, and returns a changer that can be attached hooks to hide them "if" the value was true.

For more information, see the documentation of (if:).

Example usage:

(set: $form to "human")
(unless: $form is "duck")[The cold autumn rain chills your skin.]

The (else-if: ) macro

(else-if: Boolean) Changer

This macro's result changes depending on whether the previous hook in the passage was shown or hidden. If the previous hook was shown, then this changer hides the attached hook. Otherwise, it acts like (if:), showing the attached hook if it's true, and hiding it if it's false. If there was no preceding hook before this, then an error message will be printed.

Example usage:

Your stomach makes {
(if: $size is 'giant')[
    an intimidating rumble! You'll have to eat plenty of trees.
](else-if: $size is 'big')[
    a loud growl. You're hungry for some shrubs.
](else: )[
    a faint gurgle. You hope to scavenge some leaves.
]}

Rationale:

If you use the (if:) macro, you may find you commonly use it in forked branches of source: places where only one of a set of hooks should be displayed. In order to make this so, you would have to phrase your (if:) expressions as "if A happened", "if A didn't happen and B happened", "if A and B didn't happen and C happened", and so forth, in that order.

The (else-if:) and (else:) macros are convenient variants of (if:) designed to make this easier: you can merely say "if A happened", "else, if B happened", "else, if C happened" in your code.

Details:

Just like the (if:) macro, (else-if:) only checks its condition once, when the passage or hook contaning it is rendered.

The (else-if:) and (else:) macros do not need to only be paired with (if:)! You can use (else-if:) and (else:) in conjunction with boolean variables, like so:

(set:$married to false, $date to false)
$married[You hope this warrior will someday find the sort of love you know.]
(else-if: not $date)[You hope this warrior isn't doing anything this Sunday (because \
you've got overtime on Saturday.)]

If you attach (else-if:) to a named hook, and the (else-if:) hides the hook, you can reveal the hook later in the passage by using the (show:) macro to target the hook.

if you want to conditionally display very short strings, or small values inside a macro call, you may want to use the shorter (cond:) macro instead.

See also:

(if:), (unless:), (else:), (cond:), (show:)

The (else: ) macro

(else: ) Changer

This is a convenient limited variant of the (else-if:) macro. It will simply show the attached hook if the preceding hook was hidden, and hide it otherwise. If there was no preceding hook before this, then an error message will be printed.

Example usage:

The coins fall... 
\(if: (either:false, false, false, true))
    [and both land on tails! That means you've won the bet!]
\(else: )
    [and one of them lands heads-up.]

Rationale:

After you've written a series of hooks guarded by (if:) and (else-if:), you'll often have one final branch to show, when none of the above have been shown. (else:) is the "none of the above" variant of (else-if:), which needs no boolean expression to be provided. It's essentially the same as (else-if: true), but shorter and more readable.

For more information, see the documentation of (else-if:).

Notes:

Just like the (if:) macro, (else:) only checks its condition once, when the passage or hook contaning it is rendered.

Due to a mysterious quirk, it's possible to use multiple (else:) macro calls in succession:

(set: $isUtterlyEvil to (either:true,false))
$isUtterlyEvil[You suddenly grip their ankles and spread your warm smile into a searing smirk.]
(else:)[In silence, you gently, reverently rub their soles.]
(else:)[Before they can react, you unleash a typhoon of tickles!]
(else:)[They sigh contentedly, filling your pious heart with joy.]

This usage can result in a somewhat puzzling passage source structure, where each (else:) hook alternates between visible and hidden depending on the first such hook. So, it is best avoided.

If you attach (else:) to a named hook, and the (else:) hides the hook, you can reveal the hook later in the passage by using the (show:) macro to target the hook.

See also:

(if:), (unless:), (else-if:), (cond:), (show:)

The (for: ) macro

(for: Lambda, [...Any]) Changer

Also known as: (loop:)

When attached to a hook, this repeats the attached hook, setting a temporary variable to a different value on each repeat.

Example usage:

Rationale:

Suppose you're using arrays to store strings representing inventory items, or character datamaps, or other kinds of sequential game information - or even just built-in arrays like (history:) - and you want to print out a sentence or paragraph for each item. The (for:) macro can be used to print something "for each" item in an array easily - simply write a hook using a temp variable where each item should be printed or used, then give (for:) an "each" lambda that uses the same temp variable.

Details:

If no extra values are given after the lambda (for instance, by using ... with an empty array), then nothing will happen and the attached hook will not be printed at all.

Don't make the mistake of believing you can alter an array by trying to (set:) the temp variable in each loop - such as (for: each _a, ...$arr)[(set: _a to it + 1)]. This will NOT change $arr - only the temp variable will change (and only until the next loop, where another $arr value will be put into it). If you want to alter an array item-by-item, use the (altered:) macro.

The temp variable inside the hook will shadow any other identically-named temp variables outside of it: if you (set: _a to 1), then (for: each _a, 2,3)[ (print: _a) ], the inner hook will print "2" and "3", and you won't be able to print or set the "outer" _a.

You may want to simply print several copies of a hook a certain number of times, without any particular array data being looped over. You can use the (range:) macro with it instead: (for: each _i, ...(range:1,10)), and not use the temp variable inside the hook at all.

As it is a changer macro, (for:)'s value is a changer which can be stored in a variable - this command stores all of the values originally given to it, and won't reflect any changes to the values, or their container arrays, since then.

Alternatives:

You may be tempted to use (for:) not to print anything at all, but to find values inside arrays using (if:), or form a "total" using (set:). The lambda macros (find:) and (folded:), while slightly less straightforward, are recommended to be used instead.

See also:

(find:), (folded:), (if:)

The (either: ) macro

(either: ...Any) Any

Give this macro several values, separated by commas, and it will pick and return one of them randomly.

Example usage:

Rationale:

There are plenty of occasions where you might want random elements in your story: a few random adjectives or flavour text lines to give repeated play-throughs variety, for instance, or a few random links for a "maze" area. For these cases, you'll probably want to simply select from a few possibilities. The (either:) macro provides this functionality.

Details:

This is one of the features that uses Harlowe's pseudo-random number generator. If you use (seed:) at the start of the story, the chosen value will be predetermined based on the seed string, and how many other random macros and features have been used before it.

As with many macros, you can use the spread ... operator to place all of the values in an array or dataset into (either:), and pick them randomly. (either: ...$array), for instance, will choose one possibility from all of the array contents.

If you want to pick two or more values randomly, you may want to use the (shuffled:) macro, and extract a subarray from its result.

If you want to pick a value more reliably - for instance, to pick a value randomly, but keep using that same value in subsequent visits to the passage - you may want to store an (either:) result in a variable using (set:) in an earlier passage, and use that whenever you want to use the result.

See also:

(nth:), (random:), (shuffled:), (cond:)

The (cond: ) macro

(cond: Boolean, Any, ...Any) Any

When given a sequence of booleans (the "conditions") paired with values, this provides the first value that was paired with a true condition. This can give you one value or another based on a quick check.

Example usage:

Rationale:

While the (if:), (else:) and (else-if:) macros allow blocks of passage prose to be conditionally displayed and code to be conditionally run, there are plenty of situations where you'd prefer to succinctly select values inside macro calls, or select from multiple values, without needing to write multiple (else-if:)s or (set:)s for each possibility. The (cond:) macro (short for "condition") offers such utility.

In situations where you would write something like this,

{(set:$lostTheSword to (either:true,false))
(if: not $lostTheSword)[
(set: $weapon to "a holy sword")
](else: )[
(set:$weapon to "an unholy swear-word")
]}

you could instead simply write this.

(set:$lostTheSword to (either:true,false))(set: $weapon to (cond: not $lostTheSword, "a holy sword", "an unholy swear-word"))

Details:

This macro is intended to resemble the "cond" function in Lisp, as well as the "ternary" operator in numerous other programming languages (though it does not perform short-circuiting). It also might remind you of the values given to (dm:) - a piece of metadata, followed by its matching data - except that (dm:) ties names to data, whereas this ties conditions to data.

If only one value was given to (cond:), then that value will be returned as-is.

Except for the last, every odd value given to (cond:) must be a boolean, or an error will occur.

See also:

(if:), (dm:), (nth:)

The (nth: ) macro

(nth: Number, ...Any) Any

Given a positive whole number and a sequence of values, this selects the nth value in the sequence, where n is the number. If n is larger than the number of items in the sequence, the selection loops around to the start.

Example usage:

Rationale:

This macro is designed to be used in passage prose, letting you quickly display one of a varying range of phrases or sentences based on a certain value. In addition to being useful with story variables, it's useful with the visit identifier, allowing you to vary the text shown on each subsequent visit to a passage, with more consistent variation than if you were using (either:).

However, you can use (nth:) with any kind of value, not just strings. For instance, (text-colour: (nth: $wounds, white, yellow, red)) will produce a (text-colour:) changer that differs in colour based on the number in $wounds (up to 3).

Details:

You can, of course, access a specific value in a sequence using the (a:) macro and the 's or of syntax - (a: 1,2,3)'s ($n) is functionally very similar to (nth: $n, 1, 2, 3), and other uses of the (nth:) macro. (nth:), however, allows the given value to exceed the bounds of the sequence - (nth: 4, 1, 2, 3) would produce 1, whereas (a: 1,2,3)'s 4th would produce an error.

If you wish to use (nth:) to display very large blocks of prose, you may wish to simply put that prose in hooks, and use (if:) to selectively display one, such as by writing (if: visits is 3).

If you don't want the "looping" to occur - if you want to only return the final value if the number exceeds the sequence - you can combine this macro with (min:). (nth: (min: 3, visit), "", "", "")

You may be tempted to combine this macro with (shuffled:), as in (nth: visit, ...(shuffled: "A", "B", "C")) - however, this will NOT behave any differently from just using (either:) - each visit, the (shuffled:) macro will shuffle the sequence in a different way, so you can't guarantee that different values will be shown.

See also:

(cond:), (if:), (either:)

The (verbatim: ) macro

(verbatim: ) Changer

Also known as: (v6m:)

When attached to a hook or command, the markup inside that would normally be rendered into HTML is instead presented as plain text, as if the verbatim markup was used on it.

Example usage:

(v6m: )[ \(`A`)/ ] prints a kaomoji without fear of its source being interpreted as markup.

Rationale:

Harlowe conveniently allows you to print strings containing markup and variables, such as "Your rank is ''$rank''", rendering them as if they were written directly in the passage. However, there are many situations where you would prefer not to do so, and where you can't conveniently wrap that content in the verbatim markup. Chief among these is player-inputted text: since players can write valid Harlowe markup into (prompt:) and (input-box:) elements, displaying such text could cause no end of disaster for your story. Additionally, since this text can also include unmatched verbatim markup, attempting to encase it in verbatim markup is non-trivially difficult. This macro provides an easier way to guarantee that the markup, if present, is not rendered.

In addition, you may want to write a hook without having to worry about the task of placing its contents inside verbatim markup, or write a hook containing textual references to HTML or Harlowe code. Even if it turns out to be unnecessary, having this macro on hand can be reassuring.

Details:

This macro takes no values - each changer value it produces is the same.

If you would like to use this macro to simply print a variable's contents, the (verbatim-print:) macro may be more to your liking.

See also:

(collapse:), (verbatim-print:) (verbatim-source:)

The (verbatim-print: ) macro

(verbatim-print: Any) Command

Also known as: (v6m-print:)

A convenient combination of (verbatim:) and (print:), this prints out any single argument given to it, as text, but without rendering the resulting text as markup.

Example usage:

Rationale:

In practice, this is functionally identical to a (verbatim:) changer attached to a (print:) command. However, one major difference is that this can be stored in a variable and used in passage prose by itself, without having to attach the changer each time. This scenario is especially useful when dealing with player-inputted text: rather than having to display it with two macros each time, you can simply save this command in a variable and use that variable.

Details:

As with (print:), once text is given to this command, there's no easy way to extract it from the command value without using (source:). So, you can't provide it to (upperfirst:) and other such macros. (upperfirst: (verbatim-print: $name)) will produce an error. Instead, convert the original string using (upperfirst:) before giving it to (verbatim-print:).

If you have a string you need to print frequently, and you don't want to call (verbatim-print:) every time you need to print it, you may wish to simply (set:) a (verbatim-print:) into a variable, like so: (set: $vbName to (verbatim-print:$name)). Then, you can put the command (set in that variable) into passage prose, and it will work as expected.

See also:

(verbatim:), (print:)

The (change: ) macro

(change: HookName or String, Changer or Lambda) Command

Applies a changer (or a "via" lambda producing a changer) to every occurrence of a hook or string in a passage, once.

Example usage:

Rationale:

While changers allow you to style or transform certain hooks in a passage, it can be tedious and error-prone to attach them to every occurrence as you're writing your story, especially if the attached changers are complicated. You can simplify this by storing changers in short variables, and attaching just the variables, like so:

(set: _ghost to (text-style:'outline'))
_ghost[Awoo]
_ghost[Ooooh]

Nevertheless, this can prove undesirable: you may want to not use the _ghost styling later in development, which would force you to remove the attached variables to avoid producing an error; you may want to only style a single word or phrase, and find it inconvenient to place it in a hook; you may simply not like dedicating variables to storing changers, or in placing (set:) macros at the start of your passage's prose.

Instead, you can give the hooks the name "ghost", and then (change:) them afterward like so:

|ghost>[Awoo]
|ghost>[Ooooh]
(change: ?ghost, (text-style:'outline'))

This has a few advantages. As it ties the changer styling to a hook name rather than a variable, the (change:) can be removed later without causing errors. Placing the (change:) at the end of the passage can also make the passage's source more readable, the textual content being closer to the top.

Details:

The (change:) macro can target plain text instead of hooks, much like (click:) - simply provide a string instead of a hook name. If a "via" lambda is supplied to (change:) instead of a changer, then that lambda is used to compute a changer dynamically, using the pos keyword to distinguish each hook that's enchanted. For instance, (change: "O", via (text-style:(cond: pos is an even, 'bold', 'none'))) changes only even-numbered instances of the letter "O".

Like the (replace:), (append:) and (prepend:) macros, this macro does not affect text and hooks that appear after it, as it is an immediate command that only affects what has already been rendered. For an alternative version of this macro which does affect hooks and text after it, see (enchant:).

The built-in hook names, ?Page, ?Passage, ?Sidebar and ?Link, as well as their data names like chars or lines, can be targeted by this macro, and can be styled on a per-passage basis this way.

Using (text-colour:) with this macro will let you change the colour of links inside the indicated hook, with one exception: using (change:) to change the entire passage (via ?passage or ?page) with (text-colour:) will NOT affect links. This is to allow you to re-style the entire story without having to lose the distinct colour of links compared to passage text. You can change the colour of all links using an explicit (change: ?link, (text-colour: $color)) or by using (link-style: (text-colour: $color))[= (that is, with unclosed hook markup).

You can't use this macro to change the appearance or behaviour of a completely empty hook, such as |A>[]. Completely empty hooks (that haven't had text inserted by (replace-with:) and the like) are always hidden by Harlowe.

You can use (change:) with (transition:) to add transitions to hooks or text elsewhere in the same passage – however, if the (change:) macro is run after the passage was initially rendered, the transitions will begin animating in the middle of their usual animations, or, if enough time has passed, won't run at all. For example, (event: when time > 2s)[(change:"Riddles", (t8n:"Shudder")+(t8n-time:3s))] will apply a 3-second transition to each instance of the word "Riddles" in the passage, but since 2 seconds have already passed since those words were rendered, only the last 1 second of the transition will be visible.

You cannot use (change:) with (link:), (replace:), or any of its relatives – because the enchanted hook or text is already in the passage, the link can't appear and it can't replace anything.

See also:

(enchant:), (enchant-in:), (replace:)

The (enchant: ) macro

(enchant: HookName or String, Changer or Lambda) Command

Applies a changer (or a "via" lambda producing a changer) to every occurrence of a hook or string in a passage, and continues applying that changer to any further occurrences that are made to appear in the same passage later.

Example usage:

Rationale:

This is a special version of (change:) which doesn't just perform a single transformation of a set of hooks or text - rather, like (click:), it creates an ongoing effect that constantly checks and reapplies the changers whenever new hooks or text are inserted into the passage, persisting until you navigate to another passage. Consider the following:

(enchant: ?ghost, (text-style:'outline'))
|ghost>[Awooo]
(link:">Wait.")[|ghost>[Oooowooo]]

If this were a (change:) command, the second hook revealed by the (link:) wouldn't be affected, as it is inserted into the passage after it's finished rendering. The (enchant:) macro allows you to guarantee that every hook or bit of text that you want the changer to affect is constantly affected.

Details:

The (enchant:) macro takes the same values as (change:) - a string can be given instead of a hook name, and a lambda can be given instead of a changer. See (change:)'s article for more details about these.

This macro works well in "header" or "footer" tagged passages - using a lot of (enchant:) commands to style certain words or parts of every passage, you can essentially write a "styling language" for your story, where certain hook names "mean" certain colours or behaviour. (This is loosely comparable to using CSS to style HTML class names, but exclusively uses macros.)

When targeting ?Page, ?Passage and ?Sidebar, there is generally no difference between using (enchant:) and using (change:), as there (usually) aren't any other hooks with those names in the passage.

Like (change:), you cannot use (enchant:) with (link:), (replace:), or any of its relatives.

The enchantment created by this macro cannot change the appearance or behaviour of a completely empty hook, such as |A>[]. However, once a hook stops being empty (such as when the (append:) macro appends to it), the enchantment created by this macro will automatically start applying to it.

See also:

(click:), (change:), (enchant-in:)

The (enchant-in: ) macro

(enchant-in: HookName or String, Changer or Lambda) Changer

A variation of (enchant:) and (change:), this applies a changer to every occurrence of a hook or string within just the attached hook, rather than the whole passage. As with (enchant:), the changer will be applied to every additional occurrence inserted into the attached hook.

Example usage:

(enchant:?frog, (text-style:"italic"))
"Opening remarks?"
|frog>["Crok, crok, crok."]
(enchant-in: ?frog, (text-colour:green))
["Your response?"
|frog>["Croak, croak."]
"A stunning rebuke!"
|frog>["Croooak."]]

Rationale:

While (change:) and (enchant:) both allow hooks to have changers or styles applied to them, these macros produce commands that must be placed in the passage, and which affect every match within the passage. It can sometimes be convenient to restrict the effect of (enchant:) to just matches within a single area of prose, especially when matching using strings, the ?Link hook name, or ?Page's chars. Thus, you can use (enchant-in:), attaching it to a hook that encloses the area you want it to affect. The enchantment it produces will be treated as though it didn't exist outside of the attached hook.

Details:

You can use built-in hook data names such as lines and chars with this macro, such as by (enchant-in: ?page's lines, $changer), which will style all of the lines in the attached hook with $changer. However, this construction appears counterintuitive when written out - the HookName selects all of the lines in the page, but only those within the attached hook are styled. So, more readable shorthand macros exist for both of these - (line-style:) and (char-style:) - which you ought to use instead.

This macro takes the same values as (enchant:) and (change:), and will produce the same errors for the same values. So, (link:), (replace:), or any of its relatives cannot be given as the second value, and neither can a lambda that doesn't produce a changer.

Note that this macro can only affect explicit hooks or string occurrences, and can't affect just "part" of a target. For instance, (enchant-in: ?page, (background:red))[DANGER] will NOT turn the background of the attached hook red, but (enchant-in: ?page's lines, (background:red))[DANGER] will (because the text "DANGER" is a line of text, and is thus targeted by ?page's lines).

This enchantment will be listed in the "Enchantments" tab of the Debug Mode panel when it's active, alongside enchantments created by (enchant:).

Due to Harlowe engine limitations, this currently does NOT work when created by a lambda given to (enchant:) or (change:), such as in (enchant: ?passage, via (enchant-in:?frogs,(bg:(hsl:pos*30,0.5,1)))).

See also:

(enchant:), (change:), (link-style:)

The (hooks-named: ) macro

(hooks-named: String) HookName

When given a string, this creates a HookName from it. This can be used to dynamically create HookNames.

Example usage:

|oracle)["I scry with sticks, not bones."]|mage)["No teeth in the jawbones?"]|bodyguard)["Don't sift through rot."]

(set: $companionType to "bodyguard")
(link:"Investigate the bones")[(show:(hooks-named:$companionType))]

Rationale:

The standard syntax for referring to hooks, in macros such as (replace:), (change:) or (show:), is to write a HookName, such as ?door. That syntax, though, requires that you hard-code the name of the hook. This macro lets you construct a HookName from one or more existing strings or other variables, so that the exact hook referenced depends on the game state.

This macro is called (hooks-named:) to avoid confusion with (hook:), and also to convey that a HookName will refer to any number of hooks as long as they have the same name.

Details:

Note that the HookNames produced by this macro have the same functionality as other HookNames. In particular, you can specify the 1st hook, 2ndlast and so forth by writing, for instance, (hooks-named: "A")'s 2ndlast. Also note that the built-in HookNames can be constructed with this macro - (hooks-named:"passage") is the same as ?passage.

If an empty string is given, then this will cause an error.

See also:

(hook:)

The (border: ) macro

(border: String, [String], [String], [String]) Changer

Also known as: (b4r:)

A changer macro that applies a CSS border to the hook.

Example usage:

(b4r:"dotted")[I love you!
I want to be your wife!]

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is.

This macro affects the style of the border, and accepts the following border names.

String Example
"none" Example text
"solid" Example text
"dotted" Example text
"dashed" Example text
"double" Example text
"groove" Example text
"ridge" Example text
"inset" Example text
"outset" Example text

The "none" type can be used to remove a border that another changer may have included. NOTE: As of Harlowe 3.2.2, this can only be used to remove borders from combined changers, such as by (set: $changer to it + (b4r:"none")), and can't be used to remove borders from already-changed hooks or other structures.

The default size of the border, with no other CSS changes to any elements, is 2px (2 pixels), unless a change is applied using (border-size:).

Due to browser CSS limitations, the border will force the hook to become a single rectangular area. The hook can no longer word-wrap, and moreover occupies every line in which its text is contained. So, this changer is best suited for entire paragraphs of text (or hooks using the (box:) changer) rather than single words or phrases.

See also:

(border-size:), (border-colour:), (corner-radius:)

The (border-colour: ) macro

(border-colour: String or Colour, [String or Colour], [String or Colour], [String or Colour]) Changer

Also known as: (b4r-colour:), (border-color:), (b4r-color:)

When applied to a hook being changed by the (border:) changer, this changes the border's colour.

Example usage:

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is (or the top value if it's the only one).

Much like (text-colour:), this accepts either a Colour (such as those produced by (hsl:) or (rgb:), or plain literals like #fff), or a CSS colour string.

Certain (border:) styles, namely "ridge", "groove", "inset" and "outset", will modify the colour, darkening it for certain parts of the border to produce their namesake appearance.

Selecting "transparent" as the colour will cause the border to "disappear", but also cause the space surrounding the hook to remain.

See also:

(bg:), (text-colour:)

The (border-size: ) macro

(border-size: Number, [Number], [Number], [Number]) Changer

Also known as: (b4r-size:)

When applied to a hook being changed by the (border:) changer, this multiplies the size of the border by a given amount.

Example usage:

(b4r:"solid")+(b4r-size:4)[Do not read anything outside of this box.]

Details:

The border macros accept up to four values. These values refer to sides of a rectangle, going clockwise from the top: the first value is the top edge (12 o'clock), second is the right edge (3 o'clock), third is the bottom edge (6 o'clock), fourth is the left edge (9 o'clock). You can stop giving values anywhere. If an edge doesn't have a value, then it will use whatever the opposite edge's value is (or the top value if it's the only one).

The default size of borders added using (border:) is 2px (2 pixels). The number given is a number of CSS pixels to set the new size to. Since CSS pixels don't exactly correspond to display pixels (such as, for instance, if the browser window is zoomed in) then it's possible to have a non-whole number of CSS pixels (such as 1.5, which would, if the browser window was zoomed in to 200%, become 3 display pixels). Thus, this macro accepts numbers with fractional values. That being said, if a number lower than 0 is given, an error will be produced.

See also:

(border:), (corner-radius:), (text-size:)

The (corner-radius: ) macro

(corner-radius: Number, [Number], [Number], [Number]) Changer

When applied to a hook, this rounds the corners by the given number of pixels, causing the hook to become increasingly round or button-like.

Example usage:

(b4r:'solid')+(corner-radius:8)[Hasn't this gone on too long?]
(b4r:'solid')+(corner-radius:12)[Shouldn't you tell them the truth?]
(b4r:'solid')+(corner-radius: