Twine 2.2.0 beta 1

This post originally appeared on my Patreon.

It’s been a while but hopefully this will be a nice surprise– with the caveat that it’s a beta, and so I wouldn’t use it with your most important story of all time that’s due tomorrow morning. You can get it on Bitbucket.

What’s new?

  • Passages can now be wide, tall, or just plain large. Make important passages bigger on the map so they’re easier to find.
  • Tags can be assigned colors, which show up as a vertical stripe on a passage in the story map. Every tag doesn’t need to have a color–it’s up to you.
  • You can now press the plus and minus keys to change the zoom level of the story map.
  • A Turkish localization has been added thanks to H. Utku Maden.

Here’s what’s fixed:

  • When you zoom in and out, Twine does a better job of keeping the window centered where you were looking.
  • The zoom level you last set for a story is now remembered by Twine.
  • If for some reason a story format can’t be loaded, you can still delete it.
  • The desktop app is more tolerant of story files that it has trouble importing at startup — it keeps going, so you at least can get to the story list.
  • If the desktop app does run into problems on startup, it shows the error message it should. For a little while, it would show just a white window… not very helpful.
  • A bunch of libraries that aren’t needed anymore have been removed, so the web version’s size is a bit smaller! has changed servers

I was alerted by M.C. DeMarco on Sunday to an issue with Namely, the home page was serving up malware in one of its JavaScript files– the little script that shows the screenshots at full size when you select a thumbnail. That’s pretty bad. (To be honest, I am not sure of all the bad things that malware may have done. You can examine a copy of it on the Wayback Machine. What I saw it do was, after I clicked a link on the home page, pop open an ad under my browser window.)

After investigating things, a number of other things accelerated things from pretty bad to intensely bad:

  • Judging from the Wayback Machine, the home page began serving malware sometime in July of this year.
  • I could not determine exactly how the malware got on the site in the first place. My best guess currently is that I didn’t update one of the software packages on the site in a timely enough fashion and someone took advantage of that, but I don’t know for sure, and I don’t know how to be sure.

The one silver lining was that the issue appeared to be contained to just the home page. The online Twine editor, in particular, was untouched. The downloadable versions of Twine 2 are hosted at, so those were not in any danger, and I’ve run virus scans on the Twine 1 installers hosted on the site, which have come up clean.

I deleted the malware that same day, but given that someone had the ability to edit files at will on the web site for several months, stronger measures were called for. Conventional wisdom holds that the only way to be sure that there were no nasty surprises in store was to rebuild the server from trusted sources.

This caused some plans I had been making to accelerate. For the past several months, I’ve been discussing the possibility of moving to an Interactive Fiction Technology Foundation server with the rest of the IFTF board (I am also a member). Up until now, the web site had been hosted on Dreamhost shared hosting. Dreamhost has been generally great–in particular, I am a fan of how easy they make it to enable Let’s Encrypt on a web site–but occasionally the site would go down for 15 minutes or so, and the best answer customer support could give me was that it was consuming too many resources and had been shut off temporarily.

So, I reasoned, if I’m going to rebuild the server, I may as well do it once, not twice. Tonight, we switched from Dreamhost to a server that IFTF operates.

It has, unfortunately, not been a completely smooth transition. We weren’t yet prepared to migrate the active email addresses on (I have a couple for administrative purposes). In order to make this kind of halfway change, I had to first turn off the Dreamhost site hosting, then point the domain name to the new server in Dreamhost’s control panel.

The upshot of this is that for a while, you may see displaying a cute Dreamhost robot that’s fallen over. Hopefully the change will propagate quickly, but if it hasn’t, your patience is appreciated. I wasn’t expecting this to happen–I thought the cutover would be seamless–but I hope this won’t cause too many problems for anyone.

If you do see other issues with the web site, please drop a line to @twinethreads. I usually check in on that account at least once a day.

Collaborative Twine

Ross Ramsay, one of my Patreon supporters, asked:

I was just curious if you had any plans to incorporate Dropbox or some way to synchronize and collaborate with the program? If you eventually add some kind of mobile system for this, it would be almost needed to sync between devices. As is, I’d use it to work with others, and sync between my work and home computer. With the ability to sync in some way, you might be able to use the mobile platform instead of having a dedicated mobile version for iOS and Android.

One great weakness of Twine is that it has no real way to collaborate with other authors. When I originally created Twine as a desktop app, sharing a Twine story meant sharing the file through whatever means at hand– email, a network share, Dropbox, what have-you.

Technically speaking, this option remains in the Twine 2 era. But in the era of Google Docs and Office 365, expectations have changed. Not only do people not want to mess around with file sharing — the expectation is that data lives in the cloud, which is to say “someone else’s server” — but there’s also an expectation that multiple people should be able to edit simultaneously. These are not unfair expectations to have! In particular, the idea of simultaneous editing just makes sense. But they impose additional levels of complexity on an application.

(It reminds me of the shift from modal, text-based applications to event-driven GUI apps. I am not old enough to have actually worked on an app back then, but I remember reading about this in programming books I devoured as a kid. In both cases, a fundamental change in perspective was required.)

I decided very early on in the Twine 2 design process that I did not want to be responsible for storing stories on a server I controlled. I’ve seen other systems — and continue to see new ones crop up — that do this, and when this happens, authors become captive audiences of those who run the servers. Without some kind of financial means to keep the lights on, these services only exist so long as their maintainers have interest — and in most cases, that can be measured in years, sometimes months. Imagine writing a story that just disappeared five years later! It would be a strange, even outrageous idea in the physical world. And yet we accept such ideas without much more than a sigh when they occur online.

There were other concerns to running a centralized Twine server, too. I believe that as long as you house content, you share responsibility for it — though this does not seem to be an attitude that everyone shares these days. I’m speaking here in moral terms, not really legal ones. I understand what safe harbor provisions are. Policing content for odious stuff like racism, pedophilia, and so on is not something I want to do. It’s a thankless but necessary job.

I also looked into integrating with services like Dropbox, but in my limited exploration, I found that they expected you to have your own server backend that will talk to their services, not have clients speak directly to them. Twine 2 doesn’t have a backend. In this respect, it still resembles its TiddlyWiki parentage. An important cavest — my understanding of this might be outdated. It’s been some time since I did that exploration.

In short, I would very much like to work on a collaborative Twine server but I don’t have the time or resources to do so right now. And before I started working on it, I’d need a real plan for sustainability that I was confident in. It would only be fair to people using it.

That said, I consider this subject an open question, not a sad conclusion.

Twine 2.1.3

This originally appeared on my Patreon.

This fixes a bad bug with 2.1.2 that could cause stories to become corrupted, so if you have downloaded 2.1.2, please upgrade to this as soon as you can.

This seems like a reasonable time to talk about how testing with Twine works. Initially, the test suite used a tool called Selenium that scripted actions in a web browser and verified the results. The problem with this approach is that it has turned out to be both brittle and flaky. Brittle because I would make a change to a small part of the code and 5 tests would break sometimes, and flaky because there were often difficult-to-debug timing issues with the tests that would cause them to succeed some of the time, and not others — on the same code. Not ideal.

(I know there are lots of happy users of Selenium, to which I say: good on you. But it hasn’t been good to me.)

With the Twine 2.1 series, I’ve been focusing on unit testing, which isolates each part of the code and tests each bit of it individually. It’s been a lot more reliable thus far, but it takes time to write all the tests. I’ve been focusing unit tests on the data layer, because bugs that cause data corruption are the worst in the world. They trash users’ work and leave me feeling horribly guilty. I’m the dog that ate your homework.

The bug that was in 2.1.2 didn’t have any testing associated with it. It was a sin of omission: if a grid spacing wasn’t specified when snapping passages to the grid, passages that are automatically created as you edit your story (e.g. when you type [[A link]]) would be assigned nonsense coordinates onscreen (NaN, if you speak JavaScript), which firstly caused them to not be displayed, and secondly could cause other passages to get their coordinates trashed, as Twine tries to keep passages from overlapping each other.

(No, there isn’t any way for an end user to change grid spacing, because it’s always felt unnecessary to me… but it’s always good to leave ourselves that option should we want it someday.)

Now the offending code has checks for grid spacing, and there is a specific test to check that it handles those situations correctly. So it shouldn’t happen again. Let’s hope. There is always a degree of uncertainty with software engineering.

Twine 2.1.2

This was originally posted to my Patreon.

It’s out now! You can download it from the main site ; I’ve also posted release notes  to the wiki. This release should fix a nasty bug affecting non-English speakers and also clean up some issues related to story format versions.

The localization issue was particularly insidious. What was happening was:

1. The app would do its initial startup and load in stories from the Twine/Stories directory, creating it if needed. This step would ask the localization code to translate the name “Stories”, but because localization data hadn’t been loaded yet, it would fall back to translating it to “Stories”, no matter what langauge you wanted to use.

(The reason for this fallback is so that if something gets really messed up, you can still at least get some words on the screen, even if they’re in a language you don’t speak.)

2. The rest of the app initialization would occur, including loading localization data.

3. A user would make a change and Twine would try to save it to that Twine/Stories directory… only this time, it would translate the folder name. So if you speak French, for example, it would try to save to a nonexistent Twine/Histoires directory and fail.

The solution was simple — load the localization data as early as possible — but it was tricky to diagnose.

Looking at the release notes page tonight, I realized the last release was about three months ago. I think we’re on that pace, roughly, for future releases as well — perhaps a little bit faster.

I’m also working on Snowman 2.0 and am close to a release on that, which is exciting. It’s not too too different, but I am changing some syntax, most notably the way you create <div>s and <span>s (which is buggy in the 1.x series).

The middle beast, a tremor of trepidation in his words, said, “You aren’t from a dark planet, are you?”

“No.” Calvin shook his head firmly, though the beast couldn’t se him. “We’re–we’re shadowed. But we’re fighting the shadow.”

(Madeleine L’Engle, A Wrinkle in Time)

Twine 2.1.1 released

This was originally posted at my Patreon.

Here’s what’s inside!

  • Thanks to contributions by users like you™, Twine now speaks German and Czech, and speaks Italian better
  • The SugarCube story format has been updated to 2.14.0
  • The Harlowe story formats have been fixed so that syntax highlighting while editing passages works correctly again
  • Using Twine on Internet Explorer 11 and mobile browsers has been improved (though there’s still room for improvement on iOS)
  • A bug where renaming passages with some punctuation in their names didn’t update incoming links correctly has been fixed

Defaults, standards, and Harlowe

For this post, I’m going to steal an excellent idea Emily Short had and answer a question Jeremiah McCall, a historian who maintains Gaming the Past, asked me over email with a blog post. He wrote:

Since [the story format] Harlowe is the default [in Twine 2], I am using Harlowe and I understand that SugarCube [another story format bundled with Twine 2] is a bridge from Twine 1.x.  I have scoured the net for a description of the strengths of Harlowe and why it is now default, but I don’t seem to be able to find anything. The few comparisons of Harlowe and SugarCube seem to favor the latter — though here and there are comments that Harlowe is more robust. I’m guessing (and hoping) that Harlowe will continue to develop and continue to be the default, but am I right in that? There seems to be a fair amount of resistance. Since I’m teaching Harlowe to others in hopes they will continue to use it, I’d be grateful for a quick insight. Right now I am under the vague impression that you and Mr. Arnott intend for Harlowe to be the standard from here on. Is that true and is there a quick reason why?

A quick explanation: a story format is essentially a runtime engine for works built in Twine. It takes the passages created in Twine and displays them in a Web browser. I intended the name ‘story format’ to a bit friendlier than ‘engine,’ and formats in Twine 1 were indeed more about formatting than behavior. In Twine 2, the situation has become a bit more complex, and thus the term ‘story format’ a bit dicier. Instead of choosing colors and whether clicking a link causes the story to expand or instead replace the existing text with new text, changing story formats in Twine 2 changes the entire approach an author takes to interactivity.

There are three story formats bundled with Twine 2. People tend to think of Twine and its story formats as a monolithic development effort, but that isn’t the case — each one is developed by different people on a volunteer basis. The formats are:

  • SugarCube, maintained by Thomas Michael Edwards. As Jeremiah alludes to, this is also available for Twine 1, is very mature, and has a ton of documentation, official and otherwise.
  • Harlowe, maintained by Leon Arnott. In my opinion, it’s a daring re-imagining of how to add interactivity to text that leverages a generous spoonful of functional programming. It’s a lot younger than SugarCube, which means that there isn’t as much documentation on how to do stuff with it.
  • Snowman, maintained by me. It does the absolute minimum in terms of functionality, instead leaving that to the stalwart libraries jQuery and Underscore. It’s great if you already know JavaScript and CSS, or are interested in learning them. If you aren’t, it’s a terrible choice.

(By the way, anyone can create a story format for Twine, and adding one is as easy as copying and pasting a URL. I really enjoy Illume, for example, when proofing work.)

Anyway, Harlowe is the default story format in Twine 2. I decided to make it the default for Twine 2 not only because I had, and continue to have confidence in Leon’s skills and vision, but I also saw Twine 2 as a chance to re-examine the assumptions Twine 1 made.

There are two things in particular that I think Harlowe gets right:

  • It’s succinct and readable. I think Leon has worked out a very simple syntax for code that doesn’t interfere with the flow of reading passage text.
  • It allows you to separate code from text. Harlowe allows you to mark a section of text as a “hook” which then can have code applied to it. This means that you can keep text and code compartmentalized — which I find really handy, because the mindset I bring to writing and editing prose is vastly different from the one I bring to code.

Harlowe may be the default, but in my opinion, it’s not a standard. I say that because there is no standard. I realize this makes Twine a little bit confusing; a system like Undum has a distinct, singular look and feel. But right now, I think the compromise is worth it. SugarCube, Harlowe, and Snowman serve different audiences, which means that Twine as a whole can be useful to that many more people.

Twine 2.1.0 is released

This was originally posted at my Patreon.

At long last! I’m pretty excited about letting people at it. We spent a lot of time in beta, but I’m hoping that investment will pay off.

You can download it from the home page. If you’re upgrading, please read the release notes carefully. A lot has changed. I think you will like what’s new and I’ve tested the upgrade path as best as I could — but I still worry that there will be bumps in the road.

Two things you might enjoy: what Birdland, one of my favorite Twine works, looks like in the new version , and (if you are a webdev nerd) how technology choices have changed between 2.0 and 2.1.