Procedural Generation in Twine

By Dan Cox (Twitter,Website)


Many people know Twine as a good tool for creating quick narrative games in HTML. With its story map view showing the connection between different sections it makes for a great first-time tool for people making games. Yet, what is often forgotten when thinking about passages, the internal units of Twine, as sections of a story is that they are also an internal form of storage.

Twine works by embedding its code and content into the same HTML document. When run by a browser, the code is read and transforms its elements into a story. As the player progresses through the story, the content of different passages are shown through reading from the elements in the HTML document.

This little detail about how Twine runs is important to remember because story formats like SugarCube, one of the three built-in story formats with the current version of Twine, come with functionality to retrieve the text content of passages while it is running. As HTML elements themselves, this passages content can be retrieved and even changed. And this makes them the perfect place to place data for a story within the story itself.

The string data type in JavaScript comes with a function to split a string into an array. Named .split(), it takes a delimiter, something to mark one part of a string from another and returns an array of all of the items. Used with the function to get the contents of a passage, a long list of text data can be split into an array and then used within a story. Working just like Twine itself, the contents of passages can be inflated into memory as internal databases of names, places, or parts of things to drive more complex operations.

Through getting the contents of a passage, splitting it into an array, and then accessing its entries, it is possible to get a random entry to drive procedural generation systems. Combining these simple steps with more complex rules, picking and combining things, for example, could drive even more complex integrations where one database of entries is picked at random and used as a seed for other operations and choices.

At the same time, Twine also comes with jQuery, a JavaScript library for navigating the representation of a HTML document as its Document-Object Model (DOM). Through using this library, the idea of using passages as storages can be taken to its logical extreme: passages, because they are represented as elements in a HTML document, can be added and changed while the story is running. Using the ability of JavaScript to change the properties of objects in memory, the story format SugarCube can be hacked through adding HTML elements using jQuery and then adding new internal passage referenced to its internal representation.

Through combining jQuery with SugarCube functionality, it is possible to have a story build itself in real-time, reading and writing new code into HTML elements and linking to it internally through its JavaScript representation. Having the potential of building off of passages as forms of data and tables, it is possible to create a bare HTML document of different collections of text where code re-writes the HTML document in memory, creating new connections, parts, and generating new connections based on the code reading and rewriting itself through JavaScript, jQuery, and knowledge of how Twine handles its own internal storage.

While Twine can be thought of as a tool to create more heavily-authored stories, it can also be used to create complex procedurally generated content as well. Through recognizing that a Twine story is a HTML document, it can be treated as both something to read and something to write. Combining knowledge of how Twine works with more advanced JavaScript usage, Twine can be morphed into creating stories that can write themselves, building off of rules, databases of text, and the inherit properties of hypertexts to connect to sections within themselves.