Table of Contents

File Formats Guide

libtwee supports multiple file formats used in the Twine ecosystem. This guide explains each format and how to work with them.

Supported Formats

Format Input Output Description
Twee 3 Latest specification for Twine source files
Twine 2 HTML Compiled playable story format
Twine 2 JSON JSON export format for stories
Twine 2 Archive Collection format for multiple stories
Twine 1 HTML 🟡 Legacy HTML format (partial output support)

Twee 3 Format

Twee 3 is the primary source format for Twine stories. It's a plain text format that's human-readable and version control friendly.

Basic Structure

:: StoryTitle
My Adventure Story

:: StoryData
{
  "ifid": "12345678-1234-5678-9ABC-123456789ABC",
  "format": "Harlowe",
  "format-version": "3.3.5",
  "start": "Introduction"
}

:: Introduction
Welcome to my adventure story!

This is where your journey begins.

[[Start the adventure->Forest]]

:: Forest [location outdoor]
You are standing in a dense forest.

The trees tower above you, their leaves rustling in the wind.

* [[Go deeper into the forest->DeepForest]]
* [[Follow the path->Path]]
* [[Turn back->Introduction]]

:: DeepForest [location outdoor hidden]
You venture deeper into the mysterious forest...

Passage Headers

Passages in Twee format follow this pattern:

:: PassageName [tag1 tag2] {"position":"100,200"}
  • PassageName: Unique identifier for the passage
  • [tags]: Optional space-separated tags
  • {"metadata"}: Optional JSON metadata (position, etc.)

Story Metadata

The StoryData passage contains JSON configuration:

{
  "ifid": "Interactive Fiction ID",
  "format": "Story format name",
  "format-version": "Format version",
  "start": "Starting passage name",
  "tag-colors": {
    "important": "red",
    "optional": "blue"
  },
  "zoom": 1.0
}

Working with Twee

// Parse Twee content
string tweeContent = File.ReadAllText("story.twee");
Story story = Twee.Parse(tweeContent);

// Create Twee from Story object
string tweeOutput = story.ToTwee();
File.WriteAllText("output.twee", tweeOutput);

Twine 2 HTML Format

The compiled HTML format contains both the story data and the story format engine, creating a standalone playable file.

Structure

Twine 2 HTML files contain:

  • HTML document structure
  • Story data in a <tw-storydata> element
  • Story format JavaScript
  • Passages as <tw-passagedata> elements

Example HTML Structure

<!DOCTYPE html>
<html>
<head>
    <title>My Story</title>
</head>
<body>
    <tw-storydata name="My Story" startnode="1" 
                  ifid="12345678-1234-5678-9ABC-123456789ABC"
                  format="Harlowe" format-version="3.3.5">
        <tw-passagedata pid="1" name="Start" tags="">
            Welcome to the story!
            
            [[Continue->Next]]
        </tw-passagedata>
        <tw-passagedata pid="2" name="Next" tags="">
            This is the next passage.
        </tw-passagedata>
    </tw-storydata>
    
    <!-- Story format JavaScript would be here -->
</body>
</html>

Working with Twine 2 HTML

// Parse HTML file
string htmlContent = File.ReadAllText("story.html");
Story story = Twine2HTML.Parse(htmlContent);

// Compile story to HTML
string html = story.ToTwine2HTML();
File.WriteAllText("compiled.html", html);

Twine 2 JSON Format

JSON format provides a structured data representation of stories, useful for tool integration and data processing.

JSON Structure

{
  "name": "My Adventure Story",
  "ifid": "12345678-1234-5678-9ABC-123456789ABC",
  "format": "Harlowe",
  "format-version": "3.3.5",
  "start": "Introduction",
  "passages": [
    {
      "name": "Introduction",
      "tags": ["beginning"],
      "text": "Welcome to my adventure!\n\n[[Start->Forest]]",
      "pid": 1
    },
    {
      "name": "Forest",
      "tags": ["location", "outdoor"],
      "text": "You are in a forest.\n\n[[Continue->End]]",
      "pid": 2
    }
  ],
  "creator": "Twine",
  "creator-version": "2.8.1"
}

Working with JSON

// Parse JSON file
string jsonContent = File.ReadAllText("story.json");
Story story = Twine2JSON.Parse(jsonContent);

// Export to JSON
string json = story.ToTwine2JSON();
File.WriteAllText("exported.json", json);

Twine 2 Archive Format

Archive format allows bundling multiple stories into a single HTML file for sharing story collections.

Archive Structure

<tw-library>
    <tw-story name="Story 1" ifid="...">
        <!-- Story 1 data -->
    </tw-story>
    <tw-story name="Story 2" ifid="...">
        <!-- Story 2 data -->
    </tw-story>
</tw-library>

Working with Archives

// Parse archive
string archiveContent = File.ReadAllText("collection.html");
var stories = Twine2Archive.Parse(archiveContent);

// Create archive from multiple stories
var storyList = new List<Story> { story1, story2, story3 };
string archive = Twine2Archive.Create(storyList);
File.WriteAllText("collection.html", archive);

Legacy Twine 1 HTML

libtwee provides parsing support for legacy Twine 1 HTML files, allowing migration to newer formats.

Differences from Twine 2

  • Uses <div> elements instead of custom elements
  • Different data attribute names
  • Simpler structure
  • Limited metadata support

Migration Example

// Parse legacy Twine 1 HTML
string twine1Html = File.ReadAllText("legacy-story.html");
Story story = Twine1HTML.Parse(twine1Html);

// Convert to modern Twine 2 format
string twine2Html = story.ToTwine2HTML();
File.WriteAllText("modernized-story.html", twine2Html);

Format Conversion Examples

Complete Conversion Workflow

public class StoryConverter
{
    public static void ConvertBetweenFormats(string inputFile, string outputFile)
    {
        Story story = null;
        string extension = Path.GetExtension(inputFile).ToLower();
        
        // Parse input based on file extension
        string content = File.ReadAllText(inputFile);
        
        switch (extension)
        {
            case ".twee":
                story = Twee.Parse(content);
                break;
            case ".html":
                // Try Twine 2 first, fall back to Twine 1
                try
                {
                    story = Twine2HTML.Parse(content);
                }
                catch
                {
                    story = Twine1HTML.Parse(content);
                }
                break;
            case ".json":
                story = Twine2JSON.Parse(content);
                break;
            default:
                throw new ArgumentException($"Unsupported input format: {extension}");
        }
        
        // Generate output based on desired format
        string outputExtension = Path.GetExtension(outputFile).ToLower();
        string output;
        
        switch (outputExtension)
        {
            case ".twee":
                output = story.ToTwee();
                break;
            case ".html":
                output = story.ToTwine2HTML();
                break;
            case ".json":
                output = story.ToTwine2JSON();
                break;
            default:
                throw new ArgumentException($"Unsupported output format: {outputExtension}");
        }
        
        File.WriteAllText(outputFile, output);
        Console.WriteLine($"Converted {inputFile} to {outputFile}");
    }
}

// Usage examples
StoryConverter.ConvertBetweenFormats("story.twee", "story.html");
StoryConverter.ConvertBetweenFormats("story.html", "story.json");
StoryConverter.ConvertBetweenFormats("legacy.html", "modern.twee");

Best Practices

Format Selection

  • Use Twee 3 for source control and human editing
  • Use HTML for distribution and playing
  • Use JSON for tool integration and data processing
  • Use Archives for story collections

File Organization

project/
├── src/
│   ├── story.twee          # Source files
│   └── chapters/
│       ├── intro.twee
│       └── adventure.twee
├── build/
│   ├── story.html          # Compiled output
│   └── story.json          # Data export
└── assets/
    ├── images/
    └── styles/

Version Control

Twee format is ideal for version control:

# Track source files
git add src/*.twee

# Ignore build artifacts
echo "build/" >> .gitignore
echo "*.html" >> .gitignore