Open the [[chest]].

Clicking on the word “chest” goes to passage titled “chest”.

Open the [[pirate booty->chest]]. 

Clicking on phrase “pirate booty” goes to passage titled “chest”.

Open the [[pirate booty<-chest]]. 

Clicking on “chest” goes to passage titled “pirate booty”. Note the arrow direction.


This text is *italic*.
This text is **bold**.
#This is a top level heading
##This is a second level heading
###This is a third level heading
Line breaks

text between wavy brackets will be removed.}

Handy if you have a bunch of code in them executing silently and you want to make it legible for yourself, but take up no space in the passage on the player end.



(name: $data)[hook]

This is the basic format and names for different parts of the macro. The parentheses hold the name and the data functions are performed with, with the optional hook section in brackets to apply the macro on.

(font: "Courier New")[This text is in an anonymous hook.

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

Font macro sets the font for the text in the brackets after it.

(if: $x is 2)[
  This text is only displayed if $x is 2.
](elseif: $x > 4)[
  This text is only displayed when $x is larger than 4.
  This text is displayed whenever $x is neither 2 nor larger than 4.

If/elseif/else are our conditional statements. When your variable is a number, you don't use "" quotes around it.

(set: $howManyStars to "thousands upon thousands")

There are $howManyStars stars in the galaxy.

When your variable is a text string, you have to use "" quotes around it. Variables have dollar signs before their names and no spaces. To print a variable in the text, just include its name inline with other text. When this code is processed in a passage, it will print There are thousands upon thousands stars in the galaxy.

(set: $x to (either: 2, 3) + (random: 1, 6))
(set: $someVariable to it + 1)

(either:) macro picks one from the set of choices (they can be numbers or strings), while (random:) selects a number between the two numbers given to it. The it keyword refers to the variable currently being set in the (set:) macro and is shorthand for (set: $someVariable to $someVariable + 1), it functions like the CHANGE variable BY number data block in Scratch.


(set: $dave to (color: red) + (font: "Courier New") + (text-style: "bold"))

$dave[TG: well you see, the reason is perfectly simple and scientific.
TG: it was because shut up.
TG: shut up is why.]

You can define a variable to a style and color of text, then use the variable as shorthand for that style definition.

Colors are not text strings, they are special keywords, so they don't need quotes around them. Available colors are red, orange, yellow, green, lime, blue, navy, purple, aqua/cyan, magenta/fuchsia, white, black, grey/gray.

You can also use any hexadecemial color like #bada55 or #f00, or add two colors together (color: magenta + purple).

(set: $alertText to (text-style: "shudder") + (color:"#e74"))

$alertText[This text is red and shuddering!!]
$alertText[FUEL WARNING: the petrol is upside-down.]

(text-style:) applies a predefined text style (given as a string) to the enclosed hook. Here's a ready sampler:

(text-style: "bold")[Oh wow, look at that bold!]
(text-style: "italic")[Oh wow, look at that italic!]
(text-style: "underline")[Oh wow, look at that underline!]
(text-style: "strike")[Oh wow, look at that strike!]
(text-style: "superscript")[Oh wow, look at that superscript!]
(text-style: "subscript")[Oh wow, look at that subscript!]
(text-style: "mark")[Oh wow, look at that mark!]

(text-style: "outline")[Oh wow, look at that outline!]
(text-style: "shadow")[Oh wow, look at that shadow!]
(text-style: "emboss")[Oh wow, look at that emboss!]
(text-style: "condense")[Oh wow, look at that condense!]
(text-style: "expand")[Oh wow, look at that expand!]
(text-style: "blur")[Oh wow, look at that blur!]
(text-style: "blurrier")[Oh wow, look at that blurrier!]
(text-style: "smear")[Oh wow, look at that smear!]
(text-style: "mirror")[Oh wow, look at that mirror!]
(text-style: "upside-down")[Oh wow, look at that upside-down!]

(text-style: "blink")[Oh wow, look at that blink!]
(text-style: "fade-in-out")[Oh wow, look at that fade-in-out!]
(text-style: "rumble")[Oh wow, look at that rumble!]
(text-style: "shudder")[Oh wow, look at that shudder!]

Advanced Display Structures

She pulled out her (link: "gun")[lipstick].

(link:) macro creates a link that when clicked replaces the linked text (gun in this case) with the code inside the hook (here just text that says lipstick).

(display: "Cellar")

Inserts the passage named "Cellar" wherever used, executing any scripts contained in that passage.

There are three special passage tags that will automatically display a given passage. Passages tagged as startup will display when the game first starts. Those tagged as header are displayed before each passage, while footer passages are displayed after.

(live: 1s)[Some text to fade in a little later!(stop:)]

(live:) will display/execute the text inside its hook after the given unit of time has passed, and then repeat showing those lines of text/executing those lines of code for as long as the player is on that passage—unless/until it reaches a (stop:) macro. A live macro containing a stop macro acts as a time-release passage of text, showing up only once after a given unit of time on a certain passage.

{(set: $stuff to (array: "pitchfork", "scythe", "hoe", "AK47", "whisk"))

(set: $rotato to (print: "(link: $stuff's 1st)[$rotato](set: $stuff to (rotated: -1, ...$stuff))" ))
She brandished the $rotato menacingly.

The $rotato variable as defined creates a link that when clicked cycles through an array of options $stuff. Note that if you want to use multiple rotatos in a passage, you will have to rename $rotato and $stuff to something unique for every other instance.


(unless: (history:) contains "doohickey")[ [[Pick up the doohickey->doohickey]] ]

If the player has not visited the passage named "doohickey" show them a link to visit that passage.

(if: (history:) contains "doohickey")[ [[Throw doohickey at perp->hit enemy]] ]

If the player has already visited the passage named "doohickey" show the link in the attached hook.

(link-goto: "Anyway...", (history:)'s last)

Creates a link that says Anyway... and links to the previous page the player visited, whatever that happens to be. Note that if the previous page also has the same dynamic back link that uses (history:)'s last, the player will not be able to backtrack further using the same dynamic link, they will be stuck in a loop between these two pages.

Start Over (click:"Start Over")[(restart:)]

Reset the story and go back to the beginning (erasing the history in the process).

who do you evacuate?

(if: (history:) contains "the babies" or (passage:)'s name is "the babies")[](else:)[  [[the babies]]
](if: (history:) contains "the children" or (passage:)'s name is "the children")[](else:)[ [[the children]]
](if: (history:) contains "the teenagers" or (passage:)'s name is "the teenagers")[](else:)[ [[the teenagers]]
](if: (history:) contains "the adults" or (passage:)'s name is "the adults")[](else:)[ [[the adults]]
](if: (history:) contains "the elders" or (passage:)'s name is "the elders")[](else:)[ [[the elders]] ]

[[that's enough rescuin']]

If you want to display something when the history DOESN'T include a thing, or the current passage is not that thing.

Player input

(set: $protagonist to (prompt: "What is your name?"))

Your name is $protagonist. You were named by allegedly well-adjusted adults competent enough to sire you and rear you up to your present age. 

(prompt:) macro pops up a little window with a text field that the player needs to fill out before proceeding. Putting it inside the (set:) macro like I’ve done inputs that text into a variable that is then available to be printed on screen.

(if: (confirm: "Do you really want to punch that alligator?"))[You punched an alligator. (set: $alligatorHealth to it - 1)]
(else:)[You wisely didn't punch that alligator.(set: $alligatorInitiative to it + 1)]

(confirm:) macro pops up a little window with a question and a YES/NO or OK/CANCEL buttons (depending on the browser). It returns a TRUE/FALSE value to the script which you can (set:) into a variable for use later on, or use directly in your code like I have done here: i plugged it straight into an if/else conditional structure.


Note that any attached files WILL NOT WORK while you're editing your game on To test your game with art and music Publish to File and save it in your folder. It is a good practice to keep your game's folder structure looking something like this:
myFolder/myGameName/My Game Name.html

... so that when you're done, you can just zip up the myGameName folder and email it to your fans or upload it to the server. Since all your art is in the myGameName/assets folder and your game file (the one with the html extension) is immediately outside it, your src attributes need to start with assets/, and ditto for anything linked through CSS.

boxart.jpg sets the mood of your story in one image and intro.txt sets the tone of your story in brief. They give the player an idea of what they're getting themselves into before they jump into it.


<img src="assets/imageFileName.png" alt="image not loaded!" title="Hover tooltip">

HTML tag for an image. The src attribute is necessary (relative link to your art assets from the html file), alt is recommended (so that something shows up if the image is missing, helpful while designing and troubleshooting) and title is optional.


<audio src="assets/soundFile.mp3" autoplay>

Plays a sound file in a passage.

(print: "<script>$('body').append('<audio src=\"assets/soundFile.mp3\" autoplay>'\);</script>")

Plays a sound file across multiple passages.

(print: "<script>$('audio').remove(\);</script>")

Stops all sounds currently playing.

Advanced Stylesheets & Javascript

Cascading Style Sheets (CSS) make internets purty. Check out this getting started with CSS article or find more info on how to use specific properties. Also look up some hexadecimal colors or use the eyedropper tool in Photoshop.

Below is a relatively comprehensive restyle template with notes as to what different parts do.

/* background color or image of game */
body { background-color: #333; }

/* restyle the text globally, targeting the passage element */
tw-passage { color: #f00; font-family: "Cooper Black", serif; font-weight: normal; }

/* link default */
.enchantment-link, tw-link {
  color: #3fc; font-weight:bold; text-decoration:none; font-size: 150%; letter-spacing: .1em;
/* link on mouse over */
.enchantment-link:hover, tw-link:hover {
  color: #c0c;
  background: url(;
/* link when clicked */
.enchantment-link:active, tw-link:active { color:#fff; }
/* link visited */
.visited { color:#c6c; }
/* link visited mouse over */
.visited:hover { color:#c8c; }

Some further functional tricks:

/* remove UNDO/REDO buttons */
tw-sidebar { display: none; }

/* prevent player from highlighting the link text, remove the dotted outline from clicked links */
tw-link, tw-passage {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  outline: none;

Custom Fonts using Googfont

  1. Google fonts!
  2. Click Show All Styles bottom of the menu on the left to unfurl bold/italic options
  3. Click Add to collection for fonts you like. A collection list will unfurl at the bottom of the window containing your selection.
  4. When ready to check out, click Use on the right hand side of the collection list at the bottom... or click Review to compare your choices more closely.
  5. Make final selection of
    1. fonts,
    2. subsets (Latin is fine unless you need accented letters), and...
    3. add code. Make sure to copy the @import and NOT Standard or Javascript codes
  6. Paste the @import code at the very top of your story stylesheet, above all other restylings

Make note of the #4 "Integrate the fonts into your CSS" section. Copy the font-family properties into whichever element you want to restyle globally. You can also use the (font: "Some Googfont Name")[I am different.] macro to apply your font to specific bits of text.

Targeted passage styling

To enable unique per-passage styling based on passage tags, create a free-floating utility passage for the header and tag it header in the little passage tag field. This will include the passage at the top of ALL passages in your story when they are executed. In it paste this bit of code:

(print: "<script>$('html').removeClass(\)</script>")
(if: (passage:)'s tags's length > 0)[
  (print: "<script>$('html').addClass('" + (passage:)'s tags.join(' ') + "'\)</script>")

Then, when you're baking your CSS, you can pinpoint a specifically tagged page by prefacing whatever element you want to restyle with html.yourtag. If you have a passage tagged invert, your CSS would look like so:

/* defaults (with a nifty transition animation) */
body { background-color: white; transition: background-color 0.4s cubic-bezier(.55,.06,.68,.19) 0s; }
tw-passage { color: black; }

/* special */
html.invert body { background-color: black; }
html.invert tw-passage { color: white; }

Adding an imagebox

Works in concert with targeted passage styling above. To add an imagebox that is managed entirely from the stylesheets with tags on passages, use the following CSS as model:

html tw-passage::before{/* Adds box where image belongs */
  content: ""; display: block; background-color:#f00; border: 6px double #fff; margin: 0 0 30px 0; height: 500px; border-radius: 40px;
  background-size: cover;


/* html.TAG to create multiple choices */
html.something tw-passage::before{
  background:url("") center center;

Now lets break things down.

html tw-passage::before{}

adds a new item to your page called before. The curly brackets give it properties, in this case it gives the following properties

  content: ""; display: block; background-color:#f00; border: 6px double #fff; margin: 0 0 30px 0; height: 500px; border-radius: 40px;
  background-size: cover;

display: block; gives it a block shape.

background-color:#f00; changes it's color, in this case #f00 is red.

Images automatically shrink to fit available space

Add the following line in a card tagged footer:

{(print: "<script>$('tw-passage:visible img:not(.corsetted)').one('load', function() {var h; $(this).addClass('corsetted').wrap('<div class=\"corset\"></div>'); h = $(this).height(); do {h = h - 10; $(this).height(h); if( h <= 400 ) break; } while ( $(window).height() < $('tw-story').height()+150 ); }).each(function() {if(this.complete) {$(this).trigger('load'); } }); </script>")}

Alternatively, or in addition, if you're only concerned about images fitting in the horizontal bounds of the paragraphs, you can add this line of css to the styles:

  img { max-width: 100%; margin: 0 auto; width: auto; }
  /* margin bit centers the image horizontally */