Twine for Beginners: Using Variables

Back in my first Twine for Beginners tutorial (which I recommend at least taking a glance at before tackling this one), I mentioned that it was possible to do just about anything you see in the classic Fighting Fantasy books using only passages and hyperlinks. These gamebooks use a system of numbered passages and references, and choosing which passage to turn to performs exactly the same function as choosing which hyperlink to click in a Twine game. In addition to these standard choices, however, the passages will occasionally say something like “If you have a dagger, turn to 294. If you do not have a dagger, turn to 334.”

I think if I were going to fight Eyeface McBlubberson or Beardy the Dragon Wizard then I’d probably want something bigger than a dagger, but whatever.

There’s a totally obvious way to do this in Twine, and that’s to directly copy the method used in this Fighting Fantasy book. “[[If you have a dagger, click here]]. [[If you do not have a dagger, click here]]” will do exactly the same job and involves absolutely no Twine know-how that wasn’t covered in my first tutorial. If you’re happy to simply ask the reader to keep track of their previous choices (or note things down on some kind of character/inventory sheet) and don’t fancy reading on, then you can just do that.

This isn’t reverse psychology. The Fighting Fantasy approach is entirely valid and actually has one fairly big thing going for it: it’ll work in print. Or as hyperlinks in a standard ebook. Everything from this point on will involve setting and checking variables, which more or less limits your story/game to being run in a web browser (though it will work on just about any device that lets you browse the internet, so that shouldn’t be a deal-breaker). However, variables are a whole lot more elegant than the Fighting Fantasy approach and offer some big advantages of their own: everything is handled automatically, and (since you’re not explicitly asking “Do you have X?” or “Did you do Y?”) you can keep their effects hidden from the reader. Here’s how to use them:

Step One: Set A Variable

Variables in Twine are bits of text preceded by dollar signs. In this example, I’ll be writing a game in which a key is needed to open a door, so I’ve decided to call the variable $key. I could have called it $key1, or $HouseKey, or $HOUSE_KEY, or $Bob. You can name a variable anything you like, as long as the name is limited to letters, numbers and underscores. Variable names are case sensitive, so $KEY and $key and $Key could all be used as different variables in the same game. Don’t do that, though: it would be super confusing.

The variable $key is set with the macro (set: $key to false). You can tell that Twine recognises this as a macro (essentially a command) because it’s highlighted in pink and the text is purple. You can tell Twine recognises $key as a variable because the text is blue. false is grey to show it’s a boolean value.

I stick the command at the beginning of the passage just because it’s easier to keep track of what’s happening where. It also has the advantage of showing the (set:) macro in the little passage preview on the flowchart.

The macro would work exactly the same if I tagged it onto the end of the passage text, or even if I inserted it into the middle of a word. No matter where it appears in the passage, it’ll run just the same. The (set: $key to false) text won’t appear when the game is played, but if you put it on its own line then the player will see a blank line. Personally, I find that if I’ve only got one or two macros to work with, then simply cramming them at the very start of a passage (with no spaces between) is the simplest, neatest option.

Step Two: Check a Variable

To make the game check whether or not the player has a key, you’ll need to use a couple of different macros.

(if:)[] does much what you’d expect. IF some condition is true, it displays whatever appears in the single square brackets immediately after it. So (if: $key is true)[then you see the bit of the story in which the door is opened].

(if:)[] on its own is actually enough to get a working game. You could easily have one macro for (if: $key is true)[the door opens] immediately followed by one for (if: $key is false)[the door doesn’t open] and that should cover every eventuality just as well as what you see in the screenshot. However, it’s not good practice. If you somehow ended up in a situation in which $key were neither true nor false (perhaps you forgot to set it at all!), that passage simply wouldn’t display anything and the reader would be stuck on a blank page, unable to progress.

The solution is to use another macro, (else:)[]. The text in the single square brackets after this one will appear any time the condition specified by the preceding (if:)[] isn’t the case. Here’s another example:

(if: $BearsPoopInTheWoods is true)[Bears poop in the woods.](else:)[I guess bears don’t poop in the woods after all.]

This is going a little beyond what we need to get Vinnie back inside his house, but you can chain even more of these together by using (elseif:)[], which can be used as many times as you like in between the two. Say we had a more complicated scenario in which Vinnie could find a hairpin to open the door instead of a key:

(if: $key is true)[Vinnie opens the door with the key.](elseif: $hairpin is true)[Vinnie opens the door with the hairpin.](else:)[Vinnie can’t open the door.]

This handles literally every eventuality. If Vinnie has the key, he uses the key. If he has the key and the hairpin, he still uses the key. If he doesn’t have the key but does have the hairpin, he uses the hairpin. If anything else is the case – even if neither $key nor $hairpin are explicitly false – he can’t open the door.

But at this point we still don’t have a passage that actually gives Vinnie the key in the first place. We should probably fix that.

Step Three: Set Another Variable

There’s actually no reason this couldn’t have been Step Two, but since it’s exactly the same process as in Step One I figured I’d shake things up a bit by explaining (if:)[] and (else:)[] in the middle.

(set: $key to true) takes the $key variable we already created and changes it from false to true. This means that after visiting the bottom of the garden, the player should be able to open the door.

Strictly speaking, this would probably work even if we hadn’t already set $key to false. However, much like counting on lots of (if:)[]s to cover every option, that’s not a great idea. In fact, not setting $key to false initially is exactly the sort of thing that might cause problems with an (if: $key is true), (if: $key is false) combo later on. Taking the token amount of extra time to do things “properly” where you know how is the easiest way to avoid disaster when you do occasionally slip up. You’d only run into problems here if you’d both failed to set $key at the beginning and failed to include an (else:)[] option to cover unexpected situations, so the overall setup is really quite forgiving.

There is, however, one thing you do have to watch out for. That (set: $key to false) macro? The one we threw in at the very start? That will take effect every single time the passage is visited. This means that if the player somehow went to the bottom of the garden and found the key (which sets $key to true), then went back to the Start passage (which sets $key to false), they’d effectively lose the key again and have to go back to the bottom of the garden before they’d be able to open the door. They’d have to work that out for themselves, too – probably by chance – because there’s no indication in the game that visiting the Start passage means losing the spare key if you’ve got it.

If you look carefully at the flowchart, however, you’ll see we don’t need to worry about it in this case because it simply isn’t possible. No passage links back to Start at any point – the arrows all point away from it – and in any case the bottom of the garden passage only links to the door passage. The text that appears in the door passage when you have the key doesn’t link anywhere (it’s the end of the game), so although the door appears to link to the shed and the bottom of the garden, it’s not even possible to get to those once you have the key.

If you do intend to allow the player to return to the Start passage at any point, there’s a very handy way to set up all your variables without even touching it. This other method is also quite handy if you’ll be using a lot of variables and don’t want them cluttering up that first passage, so I figure I might as well cover it further down in this next bit:

Step BONUS: Other Stuff You Can Do With Variables

There are two things you should know about what I’ve covered in this tutorial:

  • This is enough to let you do almost anything you want with Twine.
  • There is a LOT I’ve left out.

So far you’ve only seen (set:) used to make a variable either true or false, but that’s only one sort of value a variable can have. There are actually three:

  • Boolean values: true or false (what we’ve been using so far).
  • Numbers: 1, 7, 103, 66.9 (any number you like, basically).
  • Strings: “key”, “dagger”, “Steve”, “809,000 cats in a bucket” (strings of characters – basically text).

Numbers and strings could probably do with a tutorial each, but I’ll try and at least cover one example of how you might use them right here.


Imagine a scenario in which Vinnie can choose one of three friends to help him get into his house: Sarah, Mark or Alice:

  • Sarah knows parkour and can jump from the shed to an upstairs window.
  • Mark can pick the lock.
  • Alice has magic powers and can bring the gnome to life. The gnome crawls in through a vent and opens the door from the inside.

Like I said, true and false will already let you do just about anything, and writing (set: $Sarah to true) in a “you choose Sarah” passage would then let you use (if: $Sarah is true)[Sarah gets Vinnie back inside using awesome parkour.] in the shed passage. That does the job. However, there’s an easier way.

Instead of dealing with three variables – $Sarah, $Mark and $Alice – and handling them separately, you could simply (set: $friend to “Sarah”) and then use (if: $friend is “Sarah”)[Sarah gets Vinnie back inside using awesome parkour.] to exactly the same effect. This leaves you fewer variables to deal with, and reduces the chances of you screwing something up by somehow setting two different friend variables to true when it should only ever be possible to have chosen one. It also has the advantage of letting you do something like this:

“Happy to help!” said $friend.

When actually played (assuming the player chose Sarah to help), this will read:

“Happy to help!” said Sarah.

If it’s not inside a macro that sets it or changes it or asks if it is or isn’t something, $AnyVariableYouLike just shows the value of whatever variable you’ve written.


I think this would be a good time to describe that other method of setting variables at the very beginning, so here it is:

I name this passage “startup” for clarity: it could be called anything you like. The really important thing is that I’ve added the tag startup.You can add a tag by clicking +Tag and typing whatever you like. Tags allow you to colour-code passages in the flowchart, so you might like to add an “ending” tag to every ending, for example, and assign it a colour code in order to make those passages identifiable at a glance.

The thing about  startup is that it’s a special tag meaning that Twine should run everything in that one passage at the very beginning of the game, before the Start passage is displayed. You can safely set $key to false here even if you intend the player to be able to return to the Start passage later on without $key being set to false again.

I won’t actually be using the $friend variable at any point, but I thought I’d include it in startup to illustrate one more advantage of setting your initial variables here rather than doing it within the story itself. You might notice that the entire passage is enclosed within {curly brackets}. These mean that the multiple lines inside them (spread out for ease of editing) will display on just one line when the game is actually played, which (since none of the macros will be displayed at all) effectively means this startup passage should be totally invisible when it runs at the very start: just how you want it to work! You can use the same trick if you’ve got lots of variables to deal with at the start of a story passage: just begin the actual text immediately after the closing “}”.

I’m also going to use those curly brackets to neaten up our door passage:

Things in this passage are now a little more complicated than they were before, so I’ll break down the changes.

First, we’ve got an (elseif:)[] in between the (if:)[] and the (else:)[]. This is because we still want the door to open immediately if the player has found the key, but (just like the hypothetical hairpin option), we’ve added another way in even if they don’t have it. If $rattled becomes greater than 5 at any point, the door falls off its hinges and Vinnie can get in without ever having found the key.

This is achieved by adding (set: $rattled to it + 1) inside the (else:) text at the end. What this means is that if the player visits the door passage, doesn’t have the key and hasn’t already rattled the door five times, $rattled is set to its current value, + 1 and they see the same “Vinnie can’t get into the house” text as usual.

I’ll admit that (set: $rattled to it + 1) looks kind of complicated, but you can’t just say (set: $rattled to 1) because that would set the variable to exactly 1 every single time. What you’re really doing is saying (set: $rattled to $rattled + 1), but that’s a lot of typing so it’s easiest just to replace the second $rattled with it.

These two elements combined – the (elseif:)[] macro that checks if the door has been rattled more than five times, and the (set:) macro that records another rattle if it hasn’t – are that the player can keep trying the door (whether or not they check the shed in the meantime) and eventually get in through sheer persistence. Unlike the $friend scenario, this would be very difficult to do purely with true and false, but it’s absolutely trivial when you use a variable to store a number.

You might also notice that I’ve added a link to “the door” within the (else:)[] text of “the door” just so that people don’t have to hop between passages if they want to keep trying to get into the house. It’s reflected on the flowchart as an arrow from that passage looping back to itself.

If you’d like to see how this all works in practice, you can find Variable Vinnie and the Locked Door right here online!

That’s it!

There’s still much more you could do with variables in Twine – this tutorial barely scratches the surface – but once you’ve got this far, the limit is more what you can think of to do with them than what you’ve learned so far. I said in the first tutorial that it would provide you with all the technical knowledge that went into writing Girth Loinhammer’s Most Exponential Adventure. Well, after reading this you’ll have all the technical knowledge you’d need to replicate the vast majority of my Twine works. Most don’t go beyond (set:) and (if:)[], and those that do don’t do it often.

If you’d like to know more about some of the other tools that Twine offers, the best place to check is probably the Harlowe 2.1.0 manual. Since the release of Twine 2.2.1 it’s actually a little out of date, but it’s still probably the most comprehensive documentation going. Chances are that if you’re looking for something, it’s in there. If it isn’t, do feel free to ask me: you can leave comments on this very post without an account or even an email address.


  1. Pingback: Twine for Beginners: Displaying Random Text | Damon L. Wakes
  2. New to this

    Why are there three official versions of Twine 2, and which one should I choose? What is Harlowe and Sugarcube? Are they different script dialects, renderers, or what? Which one does the online version use? This is confusing and it isn’t explained anywhere that I could find.

    • Damon L. Wakes

      Great questions! I’ll do my best to answer everything here (and might try to cover some of it in a later tutorial):

      The three versions of Twine 2 available for download on are for different types of operating system: Windows, MacOS and Linux. If you’re using Windows (Windows 7, 8 or 10 for example), you need the Windows one. If you’re using a Mac (or a MacBook etc.), you need the MacOS one. If you’re using Linux (Ubuntu, Linux Mint, Fedora, etc.), you need the Linux one. I believe that the “Download 2.2.1” link that appears above those will provide you with the appropriate version automatically: if in doubt, just click that!

      Since the online version will work regardless of what operating system you use, I highly recommend that just for having a go. It also saves the (minor) hassle of installing anything. There’s very little advantage to having Twine installed directly on your computer unless you’re doing a lot with it.

      Harlowe and Sugarcube are both story formats. They change both the appearance of a finished story, and what you type in to produce a particular result. These tutorials all assume you’re using Twine 2’s default story format, Harlowe 2.1.0. This page on the Twine Wiki should hopefully illustrate some of the differences:

      Unless you poke around and change things yourself, you’ll be using Harlowe 2.1.0 regardless of whether you’ve downloaded Twine 2 or you’re using the online version. It’s probably the easiest format to get to grips with and should do everything you want it to: in my opinion you’d need a very particular reason to opt for any other format.

      • New to this

        Thank you! That explains everything.

        Re three versions, I had somehow missed the downloads and was referring to the three source repositories, but I see I don’t have to worry about that now. 🙂

  3. Pingback: Twine for Beginners: Importing Stories | Damon L. Wakes
  4. Pingback: Twine for Beginners: Timers and Live Text | Damon L. Wakes
  5. Pingback: More places for Twine help :) – Electronic Literature & Digital Writing [2]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.