Introduction to Loreline
Loreline is an open-source scripting language for writing interactive fiction. It handles branching dialogue, player choices, characters, and story state in a syntax designed to be readable by writers while still offering real programming tools (variables, conditionals, functions) when the story needs them.
Loreline works everywhere: game engines, web apps, or standalone projects. It adapts to your tools, the stories you write stay portable.
A first look
Here is a minimal Loreline script:
The warm aroma of coffee fills the café.
barista: Hi there! How are you doing today?
choice
Having a great day
barista: Wonderful! Coffee will make it even better.
Need caffeine...
barista: Say no more! Let me help with that.
Your name is Alex, right?
barista.name = "Alex"
barista: Oh, I didn't expect you'd remember it!
Text on its own is narration. barista: ... is dialogue spoken by a character. choice presents options, and indentation determines what happens after each option is picked. That's the core pattern. The Writer's Guide covers everything in detail.
Clean yet explicit by design
Loreline keeps additional punctuation to a minimum. Narrative text and dialogue can be written as-is, without any special delimiter. Jumping to another section is just ->. Assigning a variable is just =. Conditions don't need a closing keyword. A choice option can carry an inline if that hides it when the condition isn't met.
At the same time, the main structural elements (beat, choice, character, state, if, else) are plain English keywords rather than symbols. They're easy to understand at a glance, without needing to learn what each special character means. This is a deliberate trade-off: readable words over cryptic punctuation.
Other tools lean more on symbols or delimiters: Yarn Spinner uses <<if>>...<<endif>> and <<set>>; Ink has its own set of markers for choices, knots, and diverts. Loreline tries to stay close to plain text, and the editor is smart enough to apply correct syntax highlighting from those minimal cues alone:
The café is quiet this morning.
choice
Order a coffee
coffeesOrdered += 1
if coffeesOrdered > 3
barista: <concerned> That's quite a lot of coffee today...
else
barista: One more coming right up!
Order a decaf if coffeesOrdered > 2
barista: Good call. I'll make it a nice one.
For writers, this means the script reads closer to plain language, with less visual noise between the story and the structure.
Story structure
A story is organized into beats, named sections that work like scenes or chapters. A beat can jump to another with ->, or call a sub-beat with BeatName() which returns to the caller when finished:
beat EnterCafe
The morning sun streams through the café windows.
barista: Welcome! I don't think I've seen you here before.
TakeOrder()
barista: Enjoy your coffee!
-> EndScene
beat TakeOrder
barista: So, what can I get started for you?
Characters are a first-class concept: you declare them with fields that can be read and updated throughout the story:
character barista
name: Alex
mood: friendly
friendship: 0
Persistent data lives in a state block. Conditionals (if, else if, else) let the story react to earlier decisions, and choices can have inline conditions to show or hide options dynamically.
Other features
Beyond the core syntax, Loreline also supports:
- Text tags: attach metadata like
<friendly>or<whisper>to any dialogue line, for the game engine to interpret however it wants. - Dynamic text: embed variables with
$and${}, and automatic pluralization with$count coin|coins. - Alternatives: vary text across repeated visits using five selection modes (
sequence,cycle,once,pick,shuffle). - Functions: over 60 built-in functions for math, manipulating text, arrays, and maps. You can also define your own functions, directly in a Loreline script or expose them from the host engine.
- Imports: split a story across multiple
.lorfiles to keep large projects organized. - Save and restore: serialize the interpreter's full state at any point and restore it later, covering execution position, variables, character fields, and alternative block progression.
- Localization: a first-class feature of the language: tag lines with
#keysin the script, provide.lortranslation files per language, and the interpreter substitutes them at runtime.
Platforms and integration
Loreline is written in Haxe and compiles natively to multiple targets. The API works the same way everywhere: load a Loreline script, then play it by providing callbacks for dialogue, choices, and story completion. All targets share a common codebase, which ensures consistent behaviour across languages and lets updates ship everywhere at once with no major feature differences. A suite of over 160 test files covers the runtime to help keep it reliable as the language evolves.
| Platform | Notes |
|---|---|
| JavaScript / TypeScript | Works in Node.js and the browser. Available as an npm package. |
| C# / .NET | Integrates with Unity, Godot (.NET), and any .NET project. |
| C++ | Standalone library with no external dependencies. |
| Python | Works with Python 3.8+, no external dependencies. |
| Lua | Works with Lua 5.1+, no external dependencies. |
| Haxe | Available as a haxelib package with full API access. |
See the integration guides for setup instructions for each platform.
Tooling
- VS Code extension: syntax highlighting and a live preview panel for
.lorfiles. - Online playground: write and run Loreline scripts directly in the browser.
- CLI: a command-line tool for running
.lorfiles, available for macOS, Linux, and Windows.
Comparison with Ink and Yarn Spinner
Ink and Yarn Spinner are the two other tools commonly used for interactive narrative. Here's how they compare.
This section tries to make an honest comparison, as of when this article was written, and is in no way claiming one tool is better or worse than the others. I believe there is room for diversity in software for writing interactive fiction. Loreline is just joining the party!
Ink was created by Inkle and has been used in games like 80 Days and Heaven's Vault. Its structure is built around knots (major sections), stitches (sub-sections), and a weave system for inline branching. Flow control uses diverts (-> knot_name), and choices use * (once-only) or + (sticky). Ink has a mature C# runtime with an official Unity plugin. A JavaScript port (inkjs) is also officially supported; community C++ ports exist as well, including inkcpp (a standalone library also available as a UE plugin) and Inkpot (an UE5 plugin by The Chinese Room). Ink has no built-in localization system, so developers typically build their own tooling on top of the #tag metadata.
Yarn Spinner uses a screenplay-like format organized into nodes, each delimited by --- and ===. Commands use <<double angle brackets>> and options -> (v2+). It has strong Unity integration and a visual node editor for building dialogue graphs. It is primarily a C#/.NET tool; an official C++ implementation for Unreal Engine is in active development (beta as of early 2026).
Here is the same short script written in all three languages: narration, dialogue, a choice with a conditional option, and a variable update:
state
coffeesOrdered: 0
character barista
name: Alex
beat CoffeeShop
The warm aroma of coffee fills the café.
barista: Hi there! What can I get you?
choice
A regular coffee
barista: Coming right up!
A decaf if coffeesOrdered > 2
barista: Switching to decaf? Probably wise.
coffeesOrdered += 1
if coffeesOrdered > 3
barista: That's quite a lot of coffee today...
VAR coffeesOrdered = 0
=== CoffeeShop ===
The warm aroma of coffee fills the café.
Alex: Hi there! What can I get you?
+ A regular coffee
Alex: Coming right up!
+ {coffeesOrdered > 2} A decaf
Alex: Switching to decaf? Probably wise.
-
~ coffeesOrdered = coffeesOrdered + 1
{coffeesOrdered > 3:
Alex: That's quite a lot of coffee today...
}
-> DONE
title: CoffeeShop
---
<<declare $coffeesOrdered = 0>>
The warm aroma of coffee fills the café.
Alex: Hi there! What can I get you?
-> A regular coffee
Alex: Coming right up!
-> A decaf <<if $coffeesOrdered > 2>>
Alex: Switching to decaf? Probably wise.
<<set $coffeesOrdered = $coffeesOrdered + 1>>
<<if $coffeesOrdered > 3>>
Alex: That's quite a lot of coffee today...
<<endif>>
===
All three scripts tell the same story with comparable length but different conventions. Each language has its own way of handling declarations, choices, and branching.
Detailed comparison
| Loreline | Ink | Yarn Spinner | |
|---|---|---|---|
| Structure | beat sections with indentation; nested choices and alternatives |
Knots (major sections) + stitches (sub-sections) + weave for inline branching | Named nodes, each delimited by --- / === |
| Choices | choice keyword + indentation; inline if conditions on individual options |
* (disappears once chosen) or + (always available); the marker controls whether each option reappears; weave handles local branching |
-> options with <<if>> conditions |
| Characters | First-class: declared with typed fields, readable and writable throughout the story | Handled by the host app | Handled by the game engine |
| State | Variables live in state blocks: top-level state (shared across the whole script); state inside a beat (scoped to that beat); new state (temporary, reset each time the beat runs) |
Built-in variables: VAR (global), TEMP (local), LIST (enum-like) |
Built-in variables; declared with <<declare>> |
| Text tags | Built-in <tag> on any dialogue line; passed to the host as-is without affecting flow |
#tag appended to output text |
<<command>> blocks for game instructions |
| Alternatives | Built-in with 5 modes: sequence, cycle, once, pick, shuffle |
Sequences and shuffle via & / ~ choice markers |
Not built-in; handled through custom scripting |
| Functions | 60+ built-in functions. Custom functions can be written directly in Loreline scripts | Built-in functions for common operations; custom logic requires external functions written in C# or JavaScript | Custom logic handled through C# commands and functions registered from the host app |
| Localization | Built-in: #key tags in-script + .lor translation files per language |
Not built-in; teams typically build on #tag metadata |
CSV string table export/import |
| Targets | JS, TS, C#, C++, Python, Lua, Haxe, all from a single Haxe codebase | C# official (Unity); JS official (inkjs); C++ community only (inkcpp, Inkpot) | C# / .NET official (Unity); C++ for UE in active development (beta) |
Ink and Yarn Spinner are mature, well-documented, and have been used in shipped games for years. Loreline is newer and takes a different approach: indentation-based structure, characters and state as core language features, and native compilation to multiple platforms from a single codebase. The best choice comes down to team familiarity, target engine, and which scripting style fits how you want to write.
Try Loreline
The fastest way to get started is the Playground, where you can write and run Loreline scripts directly in the browser, no setup needed. When you're ready to go further, the Writer's Guide covers the full language step by step.