Writing “Vault guard,” a short interactive fiction story

Over the weekend, I wrote Vault guard, a short interactive fiction story about a vault, its guard, a man in black, a sword, a lunch, a tavern, and a treasure. I’ve never written anything in the IF genre before, so I wasn’t aware of the tooling that’s available1, which includes Twine and others. This post, then, is about how I authored “Vault guard” in plain HTML2, and made it work as an IF story.

Light on <details>

The first thing I thought to try was to use HTML5’s <details> element, which is for text that can optionally show, well, details:

Like this (click me!) You can see the details of this section here. It can include arbitrary block-level content, including other details.

When I’m writing this post, here’s what I write3:

<summary>Like this (click me!)</summary>
You can see the details of this section here.
It can include arbitrary block-level content,
<em>including other details</em>.

I thought I could nest <details> within each other to allow the reader to branch the narrative, and it would’ve worked, except for two things:

  1. It was too hard to see which branches had been chosen, which hadn’t, and which were yet to be chosen
  2. There was no way to build a more complex relationship of branches – nesting <details> only allows for a descendent-tree-type structure, where each branch has one and only one parent, whereas true IF story-branches are able to have multiple parents

So I realized that I couldn’t use the <details> element for my story4.

Remembering pandoc

Then I remembered that I write this blog using Hakyll, which uses Pandoc under the hood to convert Markdown5 to HTML. And the great thing about Pandoc is that it has an option (--section-divs) to wrap each section (delineated by a heading) in an HTML <section> tag – and give those <section>s custom IDs and classes!

Remembering CSS

I also remembered that CSS has (with decent adoption) the :target pseudoclass, which denotes

a unique element (the target element) with an id matching the URL’s fragment. Mozilla Development Network

The upshot is that you can style elements that are explicitly linked to differently than other elements on the page, like having them displayed instead of hidden.

Working within the system

Hakyll builds a website by mapping a set of input files to a set of output files, transforming them in a variety of ways, such as running them through Pandoc and placing the generated HTML in templates. Pandoc’s Markdown has a great feature where the author can just include vanilla HTML in a file, and it’ll be passed through verbatim to the underlying template6. So I just included a few inline CSS styles at the beginning of my file7:

.level2 { display:none; }
.level2:target { display:block; }

See, Pandoc, when run with --section-divs, not only gives the ID of the header as the ID of the section – it also sets each section’s class to the level of the header (i.e., a <h1> header will be class="level1", etc.). So the first two rules of CSS above first hide all level-two headers and their respective content, and then show the level-two sections that have been linked to.

Pandoc automatically adds identifiers to headings, or you can define a custom ID like this:

## My header {#my-cool-id}

If you look at the source of “Vault guard,” you’ll see this is the form I used. I gave each section its own (unique) ID, then wrote the section, adding links to the bottom as list items:

- [Leave your post.](#outside)
- [Stay and wait.  You made a commitment, after all.](#wait)

When a user clicks one of the options, the current section loses the :target pseudoclass, hiding it, and the section in the link becomes the new :target, showing it.

I was able to write the whole story using sections and links this way.

Start button

I realized I needed a way to show the first section of the story, since it would be hidden unless linked to directly – and most URLs would just link to the bare page, not directly to the first fragment. So I set up a start button, a great big link that says BEGIN, linking to the first8 segment9:


And I styled that button, including this with the other style at the top of the page:

## {
## a { font-size:2em; }

Of course, now I had a new problem: a great big BEGIN link at the top of every page. Unfortunately, I couldn’t figure out a way to hide it automatically by just using CSS, so I had to use a bit of javascript:

var btn = document.getElementById("start-button");
window.onload = function() {
    if(window.location.href.indexOf("#") > 0) { 
    btn.style.display  =  "none"; 
btn.onclick = function() {
    btn.style.display = "none";

Now my game could be played. There was just one more step: I had to write the damn thing!


WARNING: Spoilers ahead! Play the game first, if you don’t want to know how it ends before you do!

I began “Vault guard” with the title. It’s post number 6 of my year-long Moon photos series, where I pull a random title from a list of 400 AI-generated titles every day. I was pondering how to write something called “Vault guard” and I realized it could be an interesting IF story. So I began sketching out the (easier) tech-related bits, above, while writing the bare minimum of the story to test them on. I wrote the first two or three sections that way.

Soon, I realized that I’d need to actually sketch out, visually, the paths the story could take, or I’d get hopelessly lost in the branches. So I tore out a piece of paper and sketched what I had, and added where I could go.

A hand-drawn outline of the plot of “Vault guard”

Writing from the outline was much easier than trying to hold everything in my head (though I surprised myself with just how much did fit in there).

The rest of the story flowed from there, and the writing of it was much like the writing of a linear narrative.


There were two major differences, and so challenges, of writing this story as opposed to a traditional linear one: I had to make sure that loop-backs weren’t jarring and that all the branches closed.

For an example of the first challenge, there are a few “Outside” sections, where our Vault guard is just outside the antechamber to the Vault. They’re all pretty similar, but I had to make sure they would all work from wherever the reader could come to them from. If the reader had just gone outside after yielding to the man in black, for example, they shouldn’t be able to go back in and know, at least, that he’d be dead from the trap. Or if they met him outside when he first arrived to the Vault, then went to the Two Bucks, and then came back, they shouldn’t be able to meet him again.

The second challenge is exemplified in this Reddit exchange I had with a playtester. I’d gotten so busy finishing the story toward the end that I completely forgot to add a section! I’m really grateful to /u/evil_tugboat_capn for pointing out that issue.

Of course, I know the second challenge is mitigated by dedicated IF-authoring software, and I bet that programs like Twine have tools to help with the first. For my next story, I’m going to be looking into those tools to make it easier on myself.


Overall, writing an IF story was challenging, but like a fun challenging. I don’t write a lot of fiction, usually because it doesn’t really hold my interest – but the short scenes of IF and the nonlinear narrative allow me to be more associative, and dare I say, lyrical, which I really enjoy.

/u/evil_tugboat_capn told me that the IFComp is coming up this year, so I think I’m going to enter that (of course, I found out too late that “Vault guard” is ineligible, since it’s already published!) and see how I do.

Finally, writing it all by hand was actually pretty simple, and didn’t require I learn any new software. I’m continually impressed with the power Hakyll affords me, and how complex I can make a static webpage. I will try Twine and friends out (especially if I want to try making a parser-based IF game), but it’s also good to know that I can use plain Pandoc and CSS to get it done too. I’d just need to write a little helper script to check I don’t have any dead ends.

  1. or rather, I should say I was aware, but couldn’t be bothered to install it and figure it out for a quick one-day authoring session↩︎

  2. err, Markdown – but it converted to HTML↩︎

  3. I’ll be adding little code snippets throughout this post, but you can also view the source for this and all the pages in this site by clicking the source link on the left sidebar↩︎

  4. I still haven’t been able to find a good use for this element, by the way, even though it seems like it’d be extremely useful.↩︎

  5. and a ton of other markup languages, to; it’s great software and I highly recommend it↩︎

  6. vanilla Markdown allows this as well↩︎

  7. in my source, I minimized these little blocks to save space, and if I write any more, I’ll add them to my templates in Hakyll↩︎

  8. The link goes to the full page, instead of just the fragment, so that if someone clicks “BEGIN” from my index page, they won’t mess up the rest of the index.↩︎

  9. This method takes advantage of Pandoc’s fenced-div syntax. You could also use plain HTML.↩︎