25 May 2015
May 2015 - Night Crossing
twinejamharlowehtmljavascriptdigital

As usual, when I get too overwhelmed and stymied by stalled or over-large projects, that's probably a good time for a game jam, and luckily someone gave me the heads up that Sharpe over on the Official Twine forums was running a 10-day Twine writing jam, specifically requiring people to use the new default story format for Twine 2, Harlowe. Since I'd already been thinking I needed to make something with Twine 2, and I'd barely even looked at Harlowe, that seemed like a winner. Even better, the hard 1,000-word limit on the jam would force me to keep things slim and streamlined. Of course, that wouldn't stop me from doing a little extracurricular experimentation along the way...

The result was Night Crossing.

Part One - The Story

Ever try to tell a whole story in less than 1,000 words? Now imagine you're making it interactive using a markup language, and the markup counts against the official word count. Ho boy.

What I ended up with is probably, honestly, more like the introduction of a story than a story proper. At best, it's a short story with a very open-ended conclusion. But for 1,000 words minus Harlowe macro overhead, I'm fine with that. I think it definitely has a certain mood to it, and it uses at least a few of Harlowe's features to create conditional passages, add semi-random events, do some basic variable tracking and react accordingly, and so forth. It's not fancy but it tells a happy little story about a hopeless fate. Wheee!

But the more I looked at how Harlowe works and what could be done with it, I realized the control you have over the overall page layout and design is almost non-existent, which is certainly a shocking change for someone who had only really worked with SugarCube. I started thinking about ways to accomplish some different modifications to the page, and I started to wonder - would it be easier to just create a page first, then plug a Harlowe story into it?

To the code editor!

Part 2 - Embedding Harlowe

First, the good news. Harlowe, as of the current 1.x releases, is fairly self-contained on the page. The Harlowe format embeds its CSS and a combined Harlowe/JQuery/Sizzle minified script directly into the output file, along with a custom <tw-storydata> element containing all of your story data. Aside from that, there's hardly anything in the published file. The basic structure looks like this:

<html>
<head>
	<style/>        <!-- Harlowe's Styles -->
</head>
<body>
	<tw-story/>     <!-- Passage display area (theoretically) -->
	<tw-storydata/> <!-- Twine story passages and data-->
	<script/>       <!-- Harlowe/JQuery/Sizzle script -->
</body>
</html>
	

So conceptually, if I wanted to plug my own content into a published Harlowe story, I just needed to make a few minor modifications to the page without changing any of Harlowe's content:

<html>
<head>
	<style/>        <!-- Harlowe's CSS -->
	<style/>        <!-- My CSS -->
	<script/>       <!-- My Javascript -->
</head>
<body>
	<div/>          <!-- My Custom page layout markup -->
	<div id="..." /><!-- Harlowe container element -->
	<tw-story/>     <!-- Passage display area (theoretically) -->
	<tw-storydata/> <!-- Twine story passages and data-->
	<script/>       <!-- Harlowe/JQuery/Sizzle script -->
</body>
</html>
	

So assuming I have all of my scripts and CSS and layout worked out ahead of time, I just plug them into the page, give the Harlowe story a container to live in so it plays nice with the rest of the page, and Bob's your uncle. Right?

So, on to the bad news. I labelled that passage display area as "theoretically" for a reason. Harlowe, again as of the current 1.x releases, has a little quirk in its passage display process. It doesn't stay put, at all. In fact, it doesn't even technically stay inside the page. When Harlowe displays a new passage, it removes the current passage from the page entirely, creates a new <tw-story> element, renders the passage in it, then attaches it to the page - outside of the body. It is attached as a child of the <html> element, after the <body>. So in terms of doing a structured layout, this is not good at all.

I looked at the Harlowe 2 source, and it looks like the new logic to render a passage makes note of the containing element of the previous passage and then renders the new passage into the same parent element. That would fix this issue entirely, if it works the way I assume it would, but that doesn't help me complete this experiment before the jam ends, so it's kludge time.

Making Harlowe Play Nice

Fortunately (and more on this later), I was planning to plug a quick and dirty game engine into this page, to see if I could get something like that running alongside Harlowe. If it worked, that would in theory allow any level of fancy interactive animated media-rich HTML page to include a Harlowe story, and the story could in turn send messages to the engine via Javascript, and there would effectively be no limitation on how far that integration goes. That means I already had code running at the best FPS it could manage, and it could also check the page for a passage appearing where we don't want it, and move it where we do want it.

My particular hack, which is effective but certainly not graceful, is first to include a little CSS which makes a passage only visible when it's located in the right place on the page:

			
		tw-story {
			display: none !important;
		}
		#content > tw-story {
			display: block !important;
		}
	

So now, passages anywhere on the page are invisible, unless they're inside the #content element in my layout. Now we need to actually move the passages when they appear. Luckily for me, I already had my engine code set up to call the window.tick() function if found, so I added this to the page:

			
window.tick = function() {
	$('html>tw-story').appendTo($('#content'));
}	
	

If you're actually a Javascript coder, you're probably cringing at how inefficient it is to do this in a game engine tick function and much overhead this adds to the page, and to you I say - you're not wrong. Time limitations to get this done within the jam period led to a less-than-elegant solution, and I'm owning that. A somewhat more sane plan would be to use setInterval() to call this function periodically and check for a passage that needs to be moved maybe a couple of times per second, rather than dozens of times per second.

But more importantly - it works. I can put my content element wherever I want to on the page, and as far as the player is concerned, Harlowe stays within its designated area and plays just like it should. I ended up just hiding the sidebar (with the undo/redo buttons) because the story didn't need them, but the same idea applies there - Harlowe puts it in a <tw-sidebar> element on the page, so just put your own container in your layout and move the sidebar there the same way you move the passages.

Part 3 - Bells and Whistles

So with all that working, the other part of the experiment was to get that engine going in the background. I set up a very simple engine, basically just handling the timing loop, calculating deltas and FPS, etc., then added some niceties to make the engine actually do something:

  1. The trees going by the window aren't a gif or webm or anything - it's three copies of the same image, with tree silhouettes and transparent background, moving past the window repeatedly at slightly different vertical offsets and speeds. This is a most inefficient way of achieving this effect, but it's also a very visual way for me to confirm that the engine is happily running in the background, moving things around, keeping a decent framerate.
  2. The ambient sound of the train is an HTML audio element loading either an mp3 or ogg audio file, and the script manually manipulates its offset and volume to make it loop as cleanly as possible and fade out at the end.
  3. An opaque overlay covers the screen, and the engine can be instructed to gradually fade it in and out. These functions can be called from the story to trigger a fade in or out.

Adding the integration from my Harlowe story was easy enough - the (set:) macro in Harlowe seems to just evaluate the value you pass it as a Javascript expression, meaning whenever I need to call my fade in/out function in a passage, I just put one of these right in the passage:

			
(set: $result to window.Engine.fadeCycle())
	

Note that if you put your code in a Javascript namespace object attached to the window object, it's available everywhere by calling window.YourNamespace.yourFunction(), so you can easily call whatever functions you add to the page directly from your passages without having to worry about the execution context or scope or any of those things you may or may not have ever even thought about, depending on your familiarity with Javascript.

Happily, once all of this was wired up, it worked like a charm, and I had a Harlowe story with custom layout, styles and scripts, all working together to tell my tiny little game jam story.

The Verdict

Feeling pretty good about this one. I finished another jam in time, and one that was good for me in several ways. I worked with Harlowe for the first time and learned a lot about it, figured out how to embed it in an HTML page, continued updating my woefully out-of-date Javascript skills, and hopefully managed to string together a couple of minute's worth of story. You can play it yourself below, hopefully functional in any major current browser (though I assume results on mobile devices will be mixed at best), and you can see more discussion and other stories entered in this challenge on the forum thread.