deprecated twine page

Twine bugfix: "and" and "or" in strings in <<set>>, <<print>> and <<if>>

Update: this has been fixed in Twine 1.4, so this is no longer necessary.

As you know from reading my macro reference, code parameters passed to <<if>>, <<set>>, <<print>> and <<remember>> have their operators ("and", "or", "$", "not", etc.) converted to Javascript equivalents ("&&", "||", etc.) when they're executed.

But, due to a bug, this conversion is also applied to "operators" that are inside strings passed to these macros. So, <<print "Vast and large">> will print "Vast && large", and <<print "You find $10">> will print "You find state.history[0].variables.10".

Now, I've sent out a pullreq to the Twine GitHub repo that fixes this, but in the meantime, if you're stymied by this bug, you can fix it with this script:

Obsolete script removed: use Twine 1.4

Again, note that in some versions this will be loaded after the Start passage has rendered.

Version history:
* 2/7/13 - Bugfix for "She's cute" string.
* 5/5/13 - Bugfix.
* 25/4/13 - Initial.
Feel free to report any bugs to @webbedspace.

Twine: ever-growing Jonah (repeats previous visited passages)

Update: this has become the default behaviour in Twine 1.4, so this is no longer necessary.

This code will alter the Jonah format such that:
* All passages are now added to the bottom, regardless of where you click the link.
* Clicking a link to a previously displayed passage will display a new version of the passage at the bottom, instead of scrolling up to the old version.

This is based on a bit of code used by E. Turner in some of his Twine games. Unlike in those games, this code also preserves the "Rewind to Here" link's functionality.

Obsolete script removed: use Twine 1.4

Remember, this goes in a disconnected passage with the tag "script".

Feel free to report any bugs to @webbedspace.

Twine: eliminate the back button functionality in Sugarcane

Update: this is now built into Twine 1.4, so this script is no longer necessary. You can enable it in Twine 1.4 by writing "Undo: off" in StorySettings.

If you want to remove the browser's Back button functionality in Sugarcane, to prevent the player from rewinding or undoing moves, here is some script code that can do that.

Obsolete script removed: use Twine 1.4

Version history:

  1. 9/3/13 - Fixed several bugs. The Start passage no longer appears in browser history, Back and Return now behave differently regarding variables, and the Rewind menu items no longer appear in browser history.
  2. 26/2/13 - Initial.

Feel free to report any bugs to @webbedspace.

Twine: Custom CSS passage transitions

I've modified my previous Sugarcane passage transition code to allow you to define your own transitions with CSS.

Update: the Javascript that was on this page is now installed in Twine 1.4.

What this does is remove the default JavaScript transition from Sugarcane, and cause the outgoing passage to gain a "transition-out" class, and the incoming passage to instantly gain and lose a "transition-in" class. This enables you to define new passage transitions by styling these 3 classes using CSS.

CSS Examples

In order for there to be a transition, you must specify the transition property for your passage elements. A very basic example is as follows:

.passage {
	transition: 0.25s linear;
	-webkit-transition: 0.25s linear;
}
The transition property causes elements to transition between styles when they gain and lose them, in a specific way. In this case, it's a quarter-second, purely linear transition. (Other options than "linear" are listed here.) The maximum duration time before the departing passage is removed is 1 second.

The current versions of Opera, Firefox and IE support the plain transition property, but you need to also include the -webkit variant in order to work in Chrome and Safari (you can also include -khtml-transition for Konqueror users, few though they may be).

Now that you've defined the transition itself, you can define the styles that the passages can transition from and to.

A very basic example:

.transition-in {
	position:absolute;
	opacity:0;
}
When a new passage arrives, its style will transition from the "transition-in" style to the normal passage style. In this case, it starts invisible ("opacity:0") and fades in to visibility. Note the "position:absolute" - both "transition-in" and "transition-out" should have either position:absolute or position:fixed if you want them to neatly appear above or below each other.
.transition-out {
	position:absolute;
	opacity:0;
}

Putting identical styles for "transition-out" means that departing passages will fade out of visibility as well. With all 3 of these CSS snippets, you've replicated the "dissolve" transition linked above.

A broad number of CSS attributes can be used with this technique. Experiment!

Combining with tag-based CSS

If you use my tag-based passage CSS code, you can tie transitions to passages tagged with a particular tag. For instance, to bind the "dissolve" transition to passages tagged with "t8n-dissolve":

body[data-tags~=t8n-dissolve] .transition-in {
	position:absolute;
	opacity:0;
}
body[data-tags~=t8n-dissolve] .passage {
	transition:1s;
	-webkit-transition: 1s;
}
body[data-tags~=t8n-dissolve] .transition-out {
	position:absolute;
	opacity:0;
}
I recommend prefixing all transition-based tags with something identical, like "t8n" (a numeronym of "transition"). This way, you can then define a default transition to be used only when no "t8n" tags are present:
body:not([data-tags*=t8n]) .transition-in {
	opacity:0;
	transform: translate(0,3rem);
	-webkit-transform: translate(0,3rem);
	position:absolute;
}
body:not([data-tags*=t8n]) .passage {
	transition: 2s;
	-webkit-transition: 2s;
}
body:not([data-tags*=t8n]) .transition-out {
	opacity:0;
	transform: translate(0,-3rem);
	-webkit-transform: translate(0,-3rem);
	position:absolute;
}

(Remember that [data-tags*=t8n] selects elements whose tags contain "t8n", and :not() selects elements that do not fulfill the requirement in the brackets.)

It's important that if you use multiple transitions, the default transition should be guarded using body:not([data-tags*=t8n]), so that its code doesn't interfere with the other transitions' code.

To see an example where the first four passages are tagged "t8n-dissolve" and the rest are not, see here.

Live examples
All examples are "The Sky in the Room" by Porpentine.

Fade in (the default transition)

.transition-in {
	opacity:0;
	position:absolute;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:absolute;
}

Specifying :not(.transition-out) means that the transition is applied only for the incoming passage - the departing one instantly gets opacity:0. Note that specifying passage.transition-in would not do anything, since it is by removing the transition-in class that the transition is initiated.

Dissolve

.transition-in {
	position:absolute;
	opacity:0;
}
.passage {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	position:absolute;
	opacity:0;
}

Retro 8-bit four-step fade in

.transition-in {
	opacity:0;
	position:absolute;
}
.passage:not(.transition-out) {
	transition: 0.8s steps(3);
	-webkit-transition: 0.8s steps(3);
}
.transition-out {
	opacity:0;
	position:absolute;
}

Fade out

.transition-in {
	opacity:0;
	position:absolute;
}
.passage:not(.transition-out) {
	transition: 0s 1s;
	-webkit-transition: 0s 1s;
}
.transition-out {
	transition: 1s;
	-webkit-transition: 1s;
	opacity:0;
	position:absolute;
}

Vertical wipe
Sadly, this currently only works if you give the passage div a fixed width and height.
If you use this, replace all instances of "1200px" and "800px" with dimensions appropriate for your story.

.transition-in {
	clip: rect(0px, 1200px, 0px, 0px) !important;
}
.passage {
	clip: rect(0px, 1200px, 800px, 0px);
	width: 1200px;
	height: 800px;
	position: absolute;
	transition: 1s linear;
	-webkit-transition: 1s linear;
}
.transition-out {
	clip: rect(800px, 1200px, 800px, 0px);
}

Zoom In

.transition-in {
	opacity:0;
	transform: scale(0.8,0.8);
	-webkit-transform: scale(0.8,0.8);
	position:absolute;
}
.passage {
	width: calc(100% - 12em); // Necessary to keep the zoom origin point consistent.
	width: -webkit-calc(100% - 12em);
	transition: 0.5s ease-out;
	-webkit-transition: 0.5s ease-out;
}
.transition-out {
	opacity:0;
	transform: scale(2,2);
	-webkit-transform: scale(2,2);
	position:absolute;
}

Fast scroll up
Ideally you would use "vh" (viewport height) units instead of "rem" (root em) units, but not enough browsers support it yet. ;_;
You can also make it a slow fading scroll if you change the lengths to something short (like 3rem).

.transition-in {
	opacity:0;
	transform: translate(0,-100rem);
	-webkit-transform: translate(0,-100rem);
	position:absolute;
}
.passage {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	transform: translate(0,100rem);
	-webkit-transform: translate(0,100rem);
	position:absolute;
}

Garage door
This would work well with a story whose passage divs have a fixed width and height, but unlike the vertical wipe it isn't strictly necessary.

.transition-in {
	opacity:0;
	position:absolute;
}
.passage {
	background-color: #000;
	transition: 1s ease-in;
	-webkit-transition: 1s ease-in;
}
.transition-out {
	position:absolute;
	z-index:3;
	transform: translate(0,-200%);
	-webkit-transform: translate(0,-200%);
}

Focus

.transition-in {
	color:transparent;
	text-shadow: #fff 0 0 1em;
	position:absolute;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:absolute;
}

"Blur Merge"

.transition-in {
	color:transparent;
	text-shadow: #fff -4em 0 1em, #fff 4em 0 1em;
	position:absolute;
}
.passage:not(.transition-out) {
	transition: 1s;
	-webkit-transition: 1s;
}
.transition-out {
	opacity:0;
	position:absolute;
}

Feel free to report any bugs to @webbedspace.

Twine: <<display>> macro altered to allow code parameters

Update: this behaviour is now enabled in Twine 1.4, so this script code is no longer necessary.

The following code, when placed in a script passage, changes <<display>> so that you can use variables and operators in it, such as <<display $passage>>.

version.extensions.displayMacro={major:2,minor:0,revision:0};macros.display={handler:function(place,macroName,params,parser){
try{var output=eval(parser.fullArgs()); new Wikifier(place,tale.get(output.toString()).text);}
catch(e){throwError(place,"bad expression: "+e.message);}}};

A quick and untidy alternative to using this code is to just use <<print tale.get($passage).text>> in place of <<display $passage>>
Feel free to report any bugs to @webbedspace.

Twine: apply inline CSS to passage text

Update: the Javascript on this page is now installed in Twine 1.4, so this script code is no longer necessary.

Twine's engine is largely based on TiddlyWiki. The standard formatting codes are all TiddlyWiki syntax. Though, having a closer look at the engine source, I discovered a few other TiddlyWiki formatting codes are present in Jonah and Sugarcane - most interesting being Inline CSS, which ostensibly lets you apply inline styles around passage text. All you have to do is type "@@", then list CSS style attributes separated and terminated with semicolons, then put the passage text (including any other formatting and macros) ending with another "@@".

However, in the stable Twine versions of Jonah and Sugarcane, it is bugged - it will not work unless you include this code in a script passage:

String.prototype.unDash = function()
{
	var s = this.split("-");
	if(s.length > 1)
		for(var t=1; t < s.length; t++)
			s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
	return s.join("");
};

If you include this code, you can use it. Here are some usage examples:

@@background-color:hsl(30,50%,50%);This text has an umber background.@@

@@color:gray;text-decoration:overline;This text is gray and has a vertical line over it.@@

@@letter-spacing: -2px;This text <<timedreplace 6>>and this macro<<endtimedreplace>> will be compressed.@@

@@text-shadow: 0.1em 0.1em #333;This text will have a gray shadow.@@

@@opacity:0.5;This text and this image [img[image.png]] will be translucent.@@

You may notice that this is all functionally equivalent to simply writing raw HTML: <html><span style="background-color:hsl(30,50%,50%);">This text has an umber background.</span></html>. Except, being in HTML mode prevents you from using wiki syntax, internal links and such, so this method is both briefer and more consistent with Twine markup.

If you want to easily apply CSS to an entire group of passages without needing to copy-paste CSS code, then you can use this method and script code to do so.

Twine: apply CSS to passages with specific tags (Tag CSS)

Update: Twine 1.4 now has an easier method of using Tag CSS, so this script code is no longer necessary.

Obsolete script removed: use Twine 1.4

CSS Syntax
The selector syntax for passages with specific tags is [data-tags~=tagname]. So, if you tag a bunch of passages with "dream", you can then apply specific CSS to just those passages like this:

[data-tags~=dream] {
  color: aqua;
  text-shadow: aqua 0 0 3px;
}
The preceding code will affect both the passage div and the body element. To select those elements separately, use syntax like this:
body[data-tags~=blood] {
  background-color:red;
  color: black;
}
.passage[data-tags~=blood] {
  border: 5px solid white;
  font-size: 110%;
  width: 30em;
}
Some variations on the selector syntax exists that you might find useful:
[data-tags*=swamp] for a passage whose tags contain "swamp" (such as "grayswamp" or "swampfort").
:not([data-tags~=gold]) for a passage which does not have the tag "gold".

And, of course, you can select elements of a matching passage <div> by combining selectors with ".body", ".body .internalLink" and such:

.passage[data-tags~=cave] .body .internalLink { color: gold; }

Caveat: all of this won't work for passage text displayed with the << display >> macro unless you use something like addtag.

Selector recap:
* To apply CSS to the body element, use "body[data-tags~=tag]", "body:not([data-tags~=tag])" etc.
* To apply CSS to the passage-class element, use ".passage[data-tags~=tag]", ".passage:not([data-tags~=tag])" etc.
* To apply CSS to both, use "[data-tags~=tag]" etc.

If you like this code, consider using these macros that let you control tags inside the game.

*This is really just a workaround until browsers support CSS selector subjects. Ideally you could just do !body .passage[data-tags:], but alas, not this year.

Version history:

  1. 11/2/13 - Possibly fixed crash in situations where 'state' hadn't been initialised yet.
  2. 6/2/13 - Additional code to affect the <body> tag was added for both versions.
  3. 5/2/13 - Altered to use "data-tags" attribute rather than "tags".
  4. 4/2/13 - Split into two snippets for Twine 1.3.6 alpha and Twine stable versions.
  5. 26/1/13 - Altered to affect the Start passage.
  6. 25/1/13 - Initial.

Twine: preloading images

Update: embedded images in Twine 1.4 do not need to be preloaded, so this is not necessary if you are using those.

If you use a lot of images in your Twine game, it would be very good of you if you preloaded them at the start of the game - having to wait for images to load during a story, even momentarily, can be distracting.

Now you could bother to convert them all to inline Base64, but there's other, less intrusive ways. You could, rather, put every image in your story in invisible img tags in the Start passage:

<html>
<img src="  [url of an image ] " style="display:none;" >
...
</html>

...but of course, that requires you to manually list every image yourself. Here is my recommendation: use this JavaScript that will do it automatically, when the story starts. Just put this in a passage tagged with "script".

(function(){var r="";var s=Wikifier.formatters;for(var j=0;j<s.length;j++){if(s[j].name=="image"){r=s[j].lookahead;
break;}}var div=document.getElementById("storeArea").firstChild.nextSibling;while(div){if(r){k(new RegExp(r,"mg"),4);
}var b=String.fromCharCode(92);var u=b+"s*['"+'"]?([^"'+"']+(jpe?g|a?png|gif|bmp))['"+'"]?'+b+"s*";
k(new RegExp("url"+b+"("+u+b+")","mig"),1);k(new RegExp("src"+b+"s*="+u,"mig"),1);
div=div.nextSibling;}function k(c,e){do{var d=c.exec(div.innerHTML);if(d){var i=new Image();
i.src=d[e];}}while(d);}}());

That's all.

Update 17/2/13: This now works with images in HTML <img> tags as well.

Update 12/2/13: This will now also preload images used in CSS url( ... ) values. It will search for such values in every passage, include the stylesheet passages, script passages, and inline JavaScript.

Version history:

  1. 17/2/13 - Now works for image files specified in HTML src="..." attributes.
  2. 13/2/13 - CSS preloading now only loads JPEG, JPG, PNG, APNG, GIF and BMP files (that is to say, not font files).
  3. 12/2/13 - Now preloads images specified by CSS URL values too.
  4. 19/1/13 - Initial.
+b+

Twine: storing images inside the HTML file

Update: embedded images in Twine 1.4 already use this technology automatically.

One aspect of Twine that I very much like is that the entire game is included in a single HTML file. It provides a great convenience and advantage for maintaining and archiving the game. Some Twine games include images in the text, and in most all cases there are in the form of hyperlinks to external image files, which, being separate from the game file, are vulnerable to being lost or broken.

There is a way to store image files (among other files) inside HTML using Base64 encoding, which converts binary files into streams of ASCII. Simply use an online Base64 encoder to convert your image into a Base64 data URI, and paste that wherever you would paste a URL to an image. This works not only for the HTML img tag, but also for Twine's img markup (although it probably won't be syntax-highlighted correctly in the passage editor).

Here is an example:
[img[data:image/png;base64,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]]

(If you paste the entire data URI between the braces into your browser URL bar, you can see the image.)

Some advantages:
- Stores all the game's resources in one file.
- Images are preloaded when the game starts.

Some disadvantages:
- Base64 encoding increases the size of the images by 33%.
- Can't easily use the same image file for multiple passages - each passage must have a full copy of the file. (You can work around this with some JavaScript preprocessing, if you really need to.)
- Doesn't work with IE versions < 9... go figure.

Syndicate content
pensive-mosquitoes