
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.








