Creating your own Twine 2 Story Format: Part 1: Understanding Twine 2 HTML Structures

Creating your own Twine 2 Story Format

Part 1: Understanding Twine 2 HTML Structures

The first step to writing your own story format for Twine 2 is understanding how data is stored. In Twine 2, specific HTML elements are used to story details about the story, any Story Stylesheet data, any Story JavaScript data, and information about the passages themselves. These are detailed in the Twine 2 HTML output specification.

<tw-storydata>

All Twine 2 story data is saved within the <tw-storydata> element. Its attributes record metadata about the story and its child elements store data about the Story Stylesheet (<style>), Story JavaScript (<script>), and each passage’s own data (<tw-passagedata>). Optionally, if tag colors were used in the story, they are also encoded as <tw-tag> elements.

For example, the following is a story with no metadata, but using all optional elements within the <tw-storydata> parent element.

<tw-storydata>
  <style
    role="stylesheet"
    id="twine-user-stylesheet"
    type="text/twine-css">
  </style>
  <script
    role="script"
    id="twine-user-script"
    type="text/twine-javascript">
  </script>
  <tw-tag
    name="tagName"
    color="orange">
  </tw-tag>
  <tw-passagedata></tw-passagedata>
</tw-storydata>

<tw-passagedata>

Each passage is stored within its own <tw-passagedata> element. Its attributes encode metadata about the passage including its name (name), tags (tags), position (position), size (size) and Passage ID (pid).

For example, a passage named “Start” at the position of 100, 100, and of the normal size (100×100), would be encoded as the following:

<tw-passagedata
  name="Start"
  position="100,100"
  size="100,100">
Some content
</tw-passagedata>

Passage Content

Content within a passage is encoded as a single text child node of the <tw-passagedata> element. (The <tw-passagedata> element cannot contain child elements of any kind other than the single text child node.)

<tw-passagedata>
[[Link to another passage]]
</tw-passagedata>

Running HTML Structures

Each story format can use any other HTML it wants to show or otherwise store data while it is running. However, starting with both Harlowe 1 and Snowman 2, two of the build-in story formats of Twine 2, the running story (<tw-story) and current passage (<tw-passage>) are also special HTML elements.

<tw-story tags="">
<tw-passage tags="">Some content.</tw-passage>
</tw-story>

While running using Harlowe 1 and Snowman 2 (or later versions), data is taken out of the storage area (<tw-storydata>) and put into the current passage element (<tw-passage>).

Adding a Story Format

Story formats add their own JavaScript code after the <tw-storydata> element and within a <script> element.

For example, consider the following example project created using the Harlowe story format.

<body>
    <tw-storydata 
        name="Example HTML"
        startnode="1"
        creator="Twine"
        creator-version="2.3.5"
        ifid="35D77845-A206-422E-B63C-08EF6DE1347F"
        zoom="1"
        format="Harlowe"
        format-version="3.1.0"
        options="" hidden="">
    <style 
        role="stylesheet"
        id="twine-user-stylesheet"
        type="text/twine-css">
    </style>
    <script 
        role="script"
        id="twine-user-script"
        type="text/twine-javascript">
    </script>
    <tw-passagedata 
        pid="1" 
        name="Start"
        tags=""
        position="105,97"
        size="100,100">Some content.</tw-passagedata>
    </tw-storydata>

    <script title="Twine engine code" data-main="harlowe"></script>
   
    <tw-story tags="">
        <tw-passage tags="">
        <tw-sidebar>
            <tw-icon 
                tabindex="0" 
                class="undo" 
                title="Undo" 
                style="visibility: hidden;">↶</tw-icon>
            <tw-icon 
                tabindex="0"
                class="redo"
                title="Redo"
                style="visibility: hidden;">↷</tw-icon>
        </tw-sidebar>
        Some content.
        </tw-passage>
    </tw-story>
</body>

Examining Harlowe Example

In the above example, the metadata of the story is encoded in the <tw-storydata> element.

<tw-storydata 
        name="Example HTML"
        startnode="1"
        creator="Twine"
        creator-version="2.3.5"
        ifid="35D77845-A206-422E-B63C-08EF6DE1347F"
        zoom="1"
        format="Harlowe"
        format-version="3.1.0"
        options="" hidden="">

It was made using Harlowe 3.1.0. It was also created by Twine 2.3.5. The starting node is 1. (The starting node is which Passage ID to load first. The passage with a pid matching this number is loaded first.)

The first (and only) passage is encoded in the <tw-passagedata> element.

 <tw-passagedata 
        pid="1" 
        name="Start"
        tags=""
        position="105,97"
        size="100,100">Some content.</tw-passagedata>

The “Start” passage has the pid of 1, its name is “Start”, and its position data is 105, 97. Its content is “Some content.”

With Harlowe selected as a story format, it also uses the <tw-story> and <tw-passage> elements.

When it started, it parsed the text node of a <tw-passagedata> element matching the startnode in the <tw-storydata> element and looked for a passage with the same pid number. (That was the “Start” passage.)

This content became the same as the <tw-passage> element. It is “Some content.”, the same as the “Start” passage.

Reviewing

Twine 2 stores data in specific HTML elements. Story formats are added in a <script> element after the storage elements within HTML.

A story format reads from the attributes of the <tw-storydata> element and then starts the story based on the startnode it finds. If a passage (stored in <tw-passagedata> elements) has a pid matching the same number as the startnode attribute, it is read first.

During play, story formats parse different passages from the HTML storage area and read the contents before placing it (in the cases of Harlowe ^1.0 and Snowman ^2.0) in elements like <tw-passage> for the user to see.