Prospero (Bruno Dias); Writing with Raconteur

As one might expect of both the author and the venue, Prospero is a typographically attractive piece of work, rendered in white text with red progression links. Links that merely expand the existing text appear in bold white, instead. The distinction between stretchtext links and movement links might be old in literary hypertext terms — I’m not certain — but it’s not consistent in the Twine and Undum world. I find it very useful to know whether the link I’m clicking is going to tell me a little more information or whether it’s going to move the whole story forward.

Prospero is also a beautifully textured piece at the level of prose. Dias has a gift for the specific detail — seen in Mere Anarchy in the variety of magical tools available to the protagonist, or here in Prospero in the scenery and the protagonist’s possessions.

The original Poe story goes like this: there’s a plague in the land, but the prince Prospero has a lot of money, so he walls himself up with his favorite courtiers and various resources, and they try to keep the plague at bay. (In contrast with the religious community in Vespers, he seems untroubled by the implications of shutting out the rest of the world.) During an elaborate party Prospero throws, a creepy masked figure attends, who turns out to be the embodiment of the plague. Despite their decadence and indifference to the world outside, the whole party dies. It is possibly a story about the inevitability of death, or possibly about the comeuppance of the wealthy classes.

Dias’ version keeps a great deal of that structure, but moves the action to something closer to the modern day, with cars and modern architecture and electric lighting. He casts the player as the Red Death, with the ability to choose the final outcome. This feels like a surprising choice, given that Poe’s version is so much about inevitability. And I think it would have been fatal to the concept of the original story to give Prospero any choice about whether he lived or died. As the Red Death, we can move among different parts of Prospero’s party, meet different party-goers, and decide whether to spare them all, or one or two favored exceptions, or no one.

Two of Dias’ previous projects, Mere Anarchy and Terminator Chaser, deal with resistance against wealth and power. Prospero raises some of the same issues, particularly the idea of the rich who imagine that they can remove themselves from the rules that apply to everyone else. In contrast with Mere Anarchy, it is comparatively merciful; it allows you to grant forgiveness, if you’re so moved. But I found that I didn’t entirely want to. Perhaps my own favorite ending was to spare one woman who seemed not to belong to the wealthy decadence around her: this suggested a universe with some moral discrimination.

Prospero was built with Raconteur, Dias’ system for creating Undum content. Undum builds beautiful hypertext with elegant typography and link transitions. However, it’s not particularly easy to use out of the box, so Raconteur provides a way to build for it without having to go direct to the javascript; and Dias has released the full source for Prospero as an example case. So in addition to reviewing Prospero, I’d like to take a moment to look at the experience of writing with Raconteur here.

Setup: easy, assuming a certain amount of tech savvy; otherwise probably rather daunting. Raconteur requires you to install various packages yourself. This is very straightforward as such installations go, and some users will probably already have some of the elements. (Raconteur depends on Node.js, which I already had on my machine.) Still, this might be daunting to authors who are used to website-based tools or self-contained IDEs. The instructions for Raconteur assume that the user will, for instance, know how to invoke sudo when appropriate. You will also need a text editor to edit the text file that defines your game.

Documentation: depends what you’re looking for. Raconteur has clear, searchable documentation designed primarily as a reference for each of the programming functions of the system. It makes the most sense if you already know a little bit about programming terminology and the capacity of Undum. (At several points it sends the reader off to refer to the Undum documentation instead.) If you’re approaching it from the perspective of “how do I achieve behavior X?”, it may or may not be easy to find what you want. EDITED TO ADD: Dias also has some tutorials on his blog, however, as he notes below.

Rapid feedback: good. Once you’ve followed Raconteur’s instructions to download and install the necessary scaffolding, you can type GULP SERVER at the command line. This will open up a browser window that shows you the current behavior of your game, and that automatically updates every time you save changes to the text file defining that game. This gives the writer a nice fast cycle for viewing the result of changes and for noticing when something has gone wrong.

Error reporting: fair. If you do something wrong with Raconteur, sometimes you will get a state where you can’t progress past the opening screen because there has been an error in building the next page. When this happened to me, I got a somewhat mysterious message in the Terminal window about an unexpected carriage return, which at least told me which line to look at. On another occasion, I had a problem that was really a Javascript error, and needed to open up the Javascript console in Chrome to see what was going on. This is fine if you already know how to do this, and I don’t really expect Raconteur to come up with any special workarounds for it. But it does, again, mean that this system is likely to be most accessible to people who already have some technical experience.

Visualization: not offered. Raconteur doesn’t provide any way of visualizing the structure of the story you’ve built. Contrast Twine’s node-based presentation or inklewriter’s narrative maps (or, in parser-land, the various indices in the Inform 7 IDE). I think this wouldn’t be too hard to provide at the same level as the rest of the Raconteur toolkit — you could have a script that parsed the “choice” lines of your game script and then spat out a .dot file that the user could then view in Graphviz. This would then also auto-update itself when the user updated the content of the story file.

Presentation control: solid. There are HTML and CSS files associated with your project and you can go mess with them if you want to, but they start out looking pretty decent even without changes. There’s more freedom to change up the look than with inklewriter, StoryNexus, ChoiceScript, or Seltani; much less fuss than with Inform. I know you can make advanced tweaks with Twine, but the Raconteur/Undum model feels closest to other kinds of web development.

Automated play: not available. Some systems (Inform, ChoiceScript, Versu) let the author create one or more test scripts to verify the behavior of the game file, and/or offer the ability to auto-play the game many many times with random inputs, in order to make sure that there aren’t any dead ends or crashes. Raconteur/Undum does not provide this capability, so everything has to be tested by hand. This is not a huge deal if you are writing a small project (and Prospero is reasonably small). It becomes more of an issue the larger your work becomes and the more you rely on Undum’s procedural features (about which more in a minute).

Prose variations: okay, could still be a little smoother. Raconteur provides facilities to do replacement of text, cycling, and stretchtext/expansion (where new words are placed within an existing paragraph). It allows cycling text in a way that stores the data for later (look for “leaveMotivation” in the Prospero code for an example), or in a lighter-weight way that doesn’t preserve any state.

What it doesn’t do is make all that really easy to type casually as part of your normal sentence-composition flow — you still need to type some fairly code-like things into your text, and then go add additional information elsewhere in the script. In my experience getting to the level where the author can type sentence variants naturally in-line makes a huge difference to how much those features are used.

Still, my experience with this was more positive than my experience trying to implement similar effects in Twine (namely: “search a bunch of different web resources for a macro to handle X or Y, then discover it’s not working quite the way you hoped but that the macro formatting makes it exceptionally hard to modify yourself”).

The very baseline task of adding a new link to a new piece of text is also more effort than in Twine.

World model: no more than Undum provides (very little). By “world model”, I mean support for maps, locations, inventory, character traits, and so on. Parser IF systems tend to provide a lot of world model support; choice-based systems typically provide little or none. Undum has a concept of qualities that can be displayed to the player in the corner of the screen. Raconteur lets you check and manipulate these qualities, which is enough to let you implement a simple inventory system or RPG-style skills. However, it makes no default assumptions about the kind of world model you want to use, nor does it (to the best of my knowledge, anyway) provide the equivalent of ChoiceScript’s fairmath for manipulating stats within a set range. You could do all this yourself if you wanted, but Raconteur doesn’t offer any special help with it.

Narrative model: no more than Undum provides (but that’s more than you might have expected). By “narrative model” I mean some kind of abstract model or organizing principle about what story elements are allowed to happen when, going beyond hand-authored branching at all points. Inform has scenes. StoryNexus and Varytale have storylets with prerequisites about when those storylets are allowed to appear. inklewriter, Twine, and ChoiceScript don’t really attempt to roll in anything like this.

Undum offers a feature somewhere between StoryNexus’ full quality-based-narrative approach and the handrolled choices of other systems. If you like, you can specify that a passage of text in Undum (called a Situation) may be followed by any choice with the appropriate tag. It also allows the author to write rules about whether a given situation should be selectable, visible but not selectable, or not visible.

This means, for instance, that you could have a number of different situations all tagged “#escape” and representing different ways of getting away from the boss monster. You could then control which of those escape options are actually offered to the player, for instance by adding a “canView” requirement that only allows the player to see the Escape by Motorbike option if they previously added the motorbike to their inventory. When you also consider that scenarios can carry multiple tags, this begins to feel a bit more powerful than the standard business of making the author write every possible transition and gate by hand.

But, of course, this is also precisely the kind of context in which some automated testing would be really helpful.

Overall, I found Raconteur more accessible and easier to start with than bare Undum, and I’d recommend it to anyone who was looking to do Undum work. It’s still not really an entry-level system for non-programmer, non-techy IF authors. I find it a bit more comfortable than Twine because I’m able to express programmatically some ideas that I can only cram into Twine via brute force. In some ways it’s less comfortable than Seltani (again, for me personally), but that’s probably because I’ve logged more time coding in Python than in Javascript; on the other hand, with Raconteur I can do everything in a text file rather than through the oven mitt of web forms. So that’s a bit of a tossup.

11 thoughts on “Prospero (Bruno Dias); Writing with Raconteur”

I’m glad you liked Prospero and found Raconteur useful – all of the features you missed are also things I want myself, by the way. Unfortunately, mapping story structures is harder than it looks (the only real way to know where a situation leads is to run it, so the only way I can think of to accurately map stories is by repeatedly random walking them), and Undum doesn’t really supply the functionality needed to build the sort of testing features that ChoiceScript has – it doesn’t run on any sort of “headless mode”, nor does it have an API for examining its state from the outside.

So right now I’m paralysed by not knowing exactly how to do those things — whether to rewrite Undum from scratch to support the features I want, fork Undum pretty radically, or write an entirely new engine to serve as the basis for future Raconteur. All of which are things I’m currently finding it hard to find the time for…

Re. graphing: hm. I would have thought that you could do something like, within each situation,

— notice any “segue” transitions appearing in the content or writing sections, and draw a direct link from the current situation to the target situation;
— notice any choice transitions directly to other named situations, and again draw a direct link from the current situation to the target situation;
— notice any choice transitions to tags, and draw a link from the current situation to that tag (tags would be represented as a different shape or color of node from standard situations);
— notice any tags and draw a link from each of those tags to the current situation.

This would obviously not account for cases where the canView property of a particular situation was never reached — for that you would indeed need some kind of dynamic process to analyze a bunch of playthroughs — but it might give some sense of intended structure all the same.

But it’s quite possible I’m missing something here — obviously you know the system much better than I do.

Re. having time to tweak things — I entirely, entirely understand how that goes.

Thanks for the link to the tutorials, as well! I’d somehow missed that those existed. I’ll revise the bit on documentation above.

Well, here’s the problem with graphing: Situation content isn’t necessarily just text, it can be an arbitrary function. So you don’t really have a way of knowing what that function is going to do without either running it, or doing some analysis so complicated you might as well run it.

Links and connections between situations can be generated dynamically based on game state, so in fact one situation’s contents are not enough to predict where it points. Imagine I have a game where the PC can die, and when they do, they get sent to an afterlife that can vary based on their behaviour. So now I have a `character.sandbox.afterlife` property, and I end up with situations that link (or automatically start) whatever situation is named by that property. Any (reasonably simple) code analysis tool could not figure out what the possible values for that property would be, and so could not figure out that every situation with a death points to “heaven”, “hell” and “limbo.”

In fact, even figuring out which situations exist is not as trivial as it might seem. A pattern that I’ve found very useful is to build templates/macroids to turn some raw text into situations that conform to a particular shape, and even a limited use of that would have the simple graph-drawing tool drawing blanks immediately. And that technique is very powerful and useful, and it’s something I imagine any sort of world model implementation in Raconteur would lean on — for example, generating “pick up” and “examine” situations for all the objects in the game automatically. Remember, a Raconteur story file gets compiled into the actual code that runs in the browser; it’s much more powerful than something like a Twee file.

So while you could build a tool to handle the simple cases, it would be very easy to trip it up, and it would end up being a very limited tool; worse of all, it would easily produce misleading or useless output. One alternative would be to let people explicitly document leaps that the tool can’t figure out on its own, I guess, but I’m not sure how much better that is than just letting people manually graph their story structures. Part of the benefit of a graphing tool would be to help authors make sure that everything connects and that certain specific paths are possible.

Still — using the “random walk” method I mentioned, a graphing tool should *eventually* be a fairly “free” side-effect of testing features that I hope to have access to with Raconteur eventually. So that also stops me from writing a graphing tool based on the less useful code analysis method.

Ah. Okay, I see. For the specific points-to-a-property case, I bet there’s still a possible workaround — like, I would still find it useful to have a graph that pointed to a non-situation node called ((death)), e.g., which represented the property we were hopping to. However, the business of being able to dynamically construct new situations — which I hadn’t quite realized was possible — does sound very difficult to overcome by a static strategy.

Sorry if I seem weirdly vehement on this point: I just find that being able to see into the structure — even if the chart is a bit crufty or limited in some respects — makes my life immeasurably easier when I’m building a new design. And if I were making a long Raconteur piece, especially if I happened to just know that I myself was only going to be using segue-and-choice transitions, I’d probably wind up rolling my own Python script or something similar for this purpose.

I realize the considerations are a bit different when you’re making a general-purpose tool, though, obviously.

Thanks for the detailed discussion! This is useful (and I hope useful also for any onlookers who are considering the tool).

Undum is a game framework, not a separate game engine. The game exists only in the browser because it expects a web document, not a headless console environment, and EVERYTHING can be a function. You can even completely rewrite situation classes and the interface. You can use JS libraries for hidden object mini-games if you want to (those are definitely not provided). The automated game testing? Nearly impossible.

But! Luckily, it’s also 100% belongs to the web development area and web developers already solved that problem (kinda). You can do functional testing – using Selenium, for example. In short, you record your actions on the web page and save them. Then you collect these tests and feed them to Selenium. For each of them it starts a browser instance, copies your actions exactly and checks if everything’s okay and you see the text you’re supposed to see.

It is still an advanced web programming, yes, so you need to install and configure Selenium by yourself. (Also, tutorials not provided, but that can change in the future) It will work for everything you can imagine, even the mini-games if they’re not random.

Undum is a great engine but it’s made only for the web developers. If you want a slightly more accessible approach, there’s Squiffy.

Hm, interesting. Have you done this with Selenium and Undum? (I’ve looked at Squiffy, but it seemed very significantly less capable — which is fair enough as it was also still in early development, I think.)

Not with Undum, just a web page; my games are not that complex. Selenium is rather hard to install (though there are a lot of alternatives, just search for “functional web testing”) and it’s clunky to execute (you wait for the browser to pop up and fire the test). Usually you just record a script and add a test check at the end. The most simple test is “whether the page has that string now” but you’d have to keep track of the strings you test by.

Undum is programmed as a closure so you won’t be able to test “whether Undum is at this situation” unless you recompile the library. Still, Selenium can imitate every possible user action and has a lot of checks, so you can record a whole playthrough and search for “YOU WON”. I believe it would be a useful tool for a long ambitious game.

Interesting. I guess I’m mostly curious about how robust this script would be if you made some modifications to the game content (so that links were no longer worded the same way, no longer in the same place on the page, etc.). But definitely worth exploring. Thanks!