Twine Troubles


Photo by Daniel Schwen

The interactive fiction platform Twine has been around for many years, but recently it’s been at the centre of the DIY game creating movement. Rise of the Videogame Zinesters heralds it as an accessible development tool for those without programming experience. New voices are joining the game-making world, and the variety of Twine games is truly remarkable: personal games, satirical games, Kafkaesque games, sexual games, and many more.

Inspired by the creativity of this new wave of game makers, I decided to work on a small Twine game as a side project. I read a few tutorials, downloaded the application, and began assembling a story. Unfortunately, I started encountering minor hindrances much sooner than expected. I was digging through forums and documentation to implement functionality that I thought would be trivial. Disheartened, I frequently had to resort to kludgy workarounds.

Here are my four biggest frustrations with Twine:

No Nested Logic

Twine has built-in syntax for linking passages, displaying text conditionally and getting/setting variables. However, the syntax does not allow any of these to be nested. You can’t display a variable inside a link or a conditional statement. You also can’t embed any html inside a link, which makes image links difficult.

// Can’t print a variable in a link
[[Hello there <<print $name>>|Say Hello]]

// Can’t conditionally print a variable
<<if $name eq "Bob">>Is <<print $husband>> feeling better?<<endif>>

// Can’t embed an image in a link
[[<html><img src=”foo.jpg” /></html>|Link]]

The workaround is to write your own html in these cases, instead of using the built-in Twine syntax. You can access the Twine variables and link to passages using JavaScript, though that has its own headaches (explained in detail below.)

Verbose Macros

Twine allows you to write custom JavaScript macros, which is great! The process for doing so, however, is a tad obfuscated and verbose. To make a macro: create a new passage, add the tag “script”, then write your logic in a function with the following template:

macros['randomnumber'] = {
    handler: function(place, macroName, params, parser) {
	insertText(place, Math.random());

That’s the bare minimum script for inserting a random number. Here’s a fancy version with all the bells and whistles:

try {
  version.extensions['randomnumber'] = { major:1, minor:0, revision:0 };
  macros['randomnumber'] = {
    handler: function(place, macroName, params, parser) {
	if (params[0] === undefined) params[0] = 0;
	if (params[1] === undefined) params[1] = 1;
	var n = Math.round(Math.random()*params[1] + params[0]);
	insertText(place, n);
} catch(e) {
  throwError(place,"randomnumber error: "+e.message); 

None of it is egregiously verbose, but boilerplate phrases like “handler function place macroName params parser” are daunting. Twine is a great tool for first-time game makers, it would be nice if writing macros was equally approachable.

I also wish it were possible to import script functions from external files. That would allow authors to share and reuse modular components. A library of common functions would be a boon to those without the technical knowledge to write their own JavaScript. Dan Cox has a clever macro for loading external JavaScript libraries, which I’d love to see integrated natively.

File Format

Twine is a graphical interface wrapper for a simple plaintext format (called twee). However, the application saves stories in a .tws file, which is just a Python pickle serialization. Since this format is not human readable, it discourages the use of source control. Why bother tracking your changes when you can’t understand the incremental differences?

Thankfully, there’s an easy built-in way to get around this. In the Twine menu, call File > Export Source Code to export your story to a plaintext twee file. You can even modify that file in a text editor and reimport it back into Twine (though you’ll lose some of your previous passage arrangement).

Weak Documentation

There is some documentation for both Twine and twee. The former covers the basics and the latter alphabetically lists available functions (assuming robust JavaScript knowledge); neither is terribly useful for learning to write macros. I found the answers to my questions scattered across the TweeCode google group, Porpentine’s resource compilation and Dan Cox’s macro tutorials. Here are a few useful tricks I had to figure out the hard way:

Firstly, if you want to link to a passage (named “foo”) from embedded html (e.g. for an image map) use <html><a href="#" onclick="javascript:state.display('foo', this); return false;"> foo </a></html>. (If you neglect to return false, it’ll work on Chrome but not Firefox.)

Secondly, if you set a variable in Twine syntax using <<set $foo = true>>, you can access it from within JavaScript macros with state.history[0].variables["foo"]. You can use this to get around some of the nested logic issues I mentioned earlier.

Finally, a minor idiosyncrasy: Twine does not allow links back to the “Start” passage. Many authors get around this limitation by making a new first passage and displaying it in “Start” with <<display ActualStart>>.

Despite my criticisms, I still think Twine is a valuable and important platform. Fixing certain small issues would simply improve what the tool already does well: provide a accessible way for everyone to make personally meaningful games. Twine is free and open source with a passionate community, so I’m certain it’ll continue to improve and grow.

→ 5 CommentsTags:  ·  · 

5 Responses to “Twine Troubles”

  1. NordicNinja Says:
    May 30th, 2013 at 12:49 pm

    Are you aware of any centralized developers trying to enhance Twine?

  2. porpentine Says:
    May 30th, 2013 at 2:11 pm

    twine 2 is in the works

  3. Dan Cox Says:
    May 31st, 2013 at 8:47 pm

    I actually submitted some code that would solve the library loading problem but it was rejected because the maintainers felt it wouldn’t be used by the majority of Twine users.

    After thinking about it some, I realized I really was the edge case and not the norm. Which is, I feel, probably the case here with you too. Twine works best when it has the least to do with programming.

    I know that is strange thing to read — and stranger still for me, of all people, to write — but I have really come around to that way of thinking. Some of the macros I’ve seen, and even many I have written, are as obtuse for others to try and read as the try-catch you showed. It’s just not designed with anything more than simple macros in mind.

    And as for the documentation problem, I’m not sure how to fix that. (I’ve thought a lot about it too.) I didn’t find Twine’s official documentation helpful and ended up writing a tutorial of my own. But even then, it was hard to judge the JavaScript and CSS knowledge of the audience.

    The other major issue too, and the one I’ve seen very few people talk about openly, is the hosting problem. There doesn’t currently exist a place for people to share their Twine-created HTML files. Outside of Dropbox or other file sharing service workarounds, there isn’t a site for that. It’s the question I’ve gotten the most since making these tutorials and the one I never have an easy answer for.

    (Thanks for linking to my stuff though! It’s often very hard to tell if I’m not just shouting into the void sometimes when I post things like those.)

  4. Widing Says:
    February 11th, 2014 at 7:14 pm

    I just wanted to confirm that you are read. For someone like me, not convenient with javascript it is hard to know wether I’m bad at reading the macro codes or if they are hard. As a beginner it is hard to understand the relationships between javascript and macros in general…

    I think the css in Twine is really messy too… Why can’t one just see the whole css code from start so that one could experiment with it? Now it easily ends up in a conflicting mess…

  5. mignon Says:
    March 18th, 2014 at 10:32 pm

    I’ve been playing around with twee for quite a while. I think it’s a decent platform for Interactive Fiction. Using the SugarCube header, you can overcome some of the issues you mention (you can store passage names in variables and use those for as destinations, etc). However, that absence of a real parser does make this language quite limited.

    I think there are more advanced users playing with it than some folks realize and are trying to push it into places it wasn’t designed for initially. It just sucks that the people in charge keep blocking attempts at really opening it up. Dan’s comments above are not the first time I’ve read about people having changes refused because it wasn’t inline with the average user’s needs.

    Maybe someone should branch it, and create a developer friendly version…eventually removing tiddlywiki altogether and just keeping the basic concept. I haven’t looked at any of the general purpose JS frameworks out there, so there might be some that would make what I’m personally trying to do much easier. But, I ultimately want to tell a story…I just don’t want it to be a simple CYOA one…

Leave a Reply

© 2007-2014 Matthew Gallant, hosted by A Small Orange, powered by Wordpress, theme based on Basic.