What is DYSTEN?
DYSTEN is a visual editor for building interactive stories โ branching narratives where the reader makes choices, rolls dice, manages an inventory, and shapes the outcome. You build stories as a graph of nodes (scenes) connected by edges (transitions). Conditions and effects make the graph dynamic.
Text Adventure
Branching prose driven by choices and dice rolls. The classic interactive fiction format.
Graphic Novel
Panel-based storytelling. Each node is an illustrated page; choices branch the art.
Interactive Movie
Video clips that play per node. The viewer chooses what happens next.
Creating a project
From the Dashboard, click + New Project. You'll configure three things before entering the editor.
The Locked Tower โ a working 6-node mystery story with stat checks, item gates, and gear. Best for learning how the system fits together.
Editor layout
The editor has four zones. Understanding them makes everything faster.
Overlay panels
Three overlay panels are accessed from the bottom bar:
Nodes
Every moment in your story is a node. There are five node types โ choose based on what the moment does.
Adding & editing nodes
Click + Add Node in the canvas toolbar to open a dropdown of bare node types, or use โ Templates for pre-wired multi-node patterns. Click any node on the canvas to select it and open the Right Panel. To delete a node, select it and click the red trash button at the top of the Right Panel.
missing_target errors.Connecting nodes
Nodes connect through outcome ports โ the amber dots on the right edge of each node. Each port represents one outcome of one action.
has torch). You can trace the full story path without opening any panel.Mechanic templates
Templates are pre-wired multi-node patterns. Click โ Templates in the canvas toolbar, pick a template, fill in a few fields, and click Insert Template.
Item Gate
Requires the player to have a specific item. Branches to a success path (item optionally consumed) or a failure path.
Stat Check
Rolls a d20 against a difficulty class. Branches to Success or Failure based on the result plus the chosen stat modifier.
Combat
A fight encounter. Victory and defeat lead to separate paths; defeat applies a health penalty.
Shop
A pre-wired shop node with a continue action. Configure items for sale in the Right Panel after inserting.
Loot Drop
A loot node with a continue action. Add item keys to the loot list in the Right Panel after inserting.
Dialogue Branch
A pure narrative choice with 2โ3 options, each routing to its own placeholder node.
Writing scenes
Scene content lives in the Right Panel โ Content tab when a node is selected.
Content types
textimagevideoPlaceholder nodes
A Placeholder node is a reserved spot in your graph with no final content. Use them to map your full story structure before writing any prose, mark scenes you want to return to, and keep the graph connected while content is in draft. To promote a placeholder to a real scene, change its Node Type to scene in the Content tab and write your content.
Naming tips
- โUse names that describe the state of the world, not the action: "Cave (Lit)" not "Enter cave with torch"
- โKeep names short โ they appear on both the canvas node and the left sidebar
- โFor branching outcomes from the same scene, prefix the name: "Duel โ Victory", "Duel โ Defeat"
Player state
Player state is every variable you track โ health, gold, flags, inventory. Define these in State Definitions (bottom bar). Every key used in a condition or effect must be declared here first.
stat
A numeric value with narrative meaning. Use for health, mana, strength. Displayed as a bar or number in the player sheet.
flag
A true/false toggle. Use for events that happened: door_opened, quest_complete. Invisible to the player by default.
counter
A numeric tally used as currency or score. Use for gold, experience, kills.
inventory
Tracks item keys the player is carrying. Pairs with Gear Definitions for rich display in the player sheet.
Visibility & defaults
Each state key has a visibility setting: visible (shown in player sheet) or hidden (tracked internally, never shown). Enable Visual relevance on a stat to show it prominently (e.g. health bar at the top of the sheet).
Open State Definitions โ Defaults to set the player's initial values. Every new session starts from these.
undefined_state_key error and gameplay will behave unexpectedly.Gear & items
Gear definitions describe every physical item in the world โ weapons, armour, consumables, key items. Open State Definitions โ Gear to manage them.
Key fields
- โkey โ machine-readable identifier used in effects (
add_item: torch), conditions (has_item: torch), and loot/shop lists. - โname โ the display name shown to the player.
- โitem_type โ weapon, armor, consumable, key_item, or misc. Determines the icon in the player sheet.
- โslot โ equipment slot (e.g.
weapon,armor). Items with the same slot replace each other when equipped. - โstat_bonuses โ passive bonuses while equipped. E.g. a sword that adds +3 strength.
Genre presets
In the Gear tab, click Genre Presets to auto-populate suggested stats and gear based on your story's genre. Presets are non-destructive โ existing keys are never overwritten.
Actions & rules
Actions are the choices presented to the player. Each action has a rule that decides how the outcome is determined, and a list of outcomes that map results to transitions.
Conditions & effects
Conditions and effects are attached to transitions. Conditions decide if a path is available; effects decide what changes when it's taken.
Conditions
A condition checks a player state key against a value using an operator. Outcomes with conditions are only available when those conditions pass.
has_item: torchPlayer has this item in inventory or equipped.
door_opened = trueState key equals (or neq: does not equal) a value.
strength โฅ 12Numeric key is greater than (or equal to) a value.
health < 20Numeric key is less than (or equal to) a value.
Add multiple conditions per transition. Set logic mode to ALL (every condition must pass) or ANY (at least one).
Effects
Effects mutate player state the moment a transition is taken. You can stack multiple effects on one transition.
set door_opened = trueSet a flag or stat to an exact value.
increment gold +50Add to a numeric value.
decrement health -20Subtract from a numeric value.
add_item: lockpickPut an item into inventory.
remove_item: torchRemove an item from inventory (consumed).
equip sword โ weaponEquip an item, removing it from inventory.
Common pattern: item gate
Player must have a torch to enter the dark cave, and the torch is consumed on use:
Or use the ๐ Item Gate template โ it builds this exact pattern in one click.
Shop & loot nodes
Shop nodes
A Shop node lets players buy items. To configure one:
Loot drop nodes
A Loot node grants items automatically when the player arrives. No choice is presented โ items go straight to inventory. Select the node โ Right Panel โ Content tab โ Loot Items section. Add item keys from your Gear Definitions.
item_never_acquirable warning.Testing & validation
Test Mode
Click โถ Test Mode in the TopBar to play through your story in a side panel without leaving the editor. Your node graph stays visible โ as you make choices, the currently active node highlights on the canvas. Test Mode is separate from the full Play view โ it's faster and keeps your editor context.
Validation panel
Open the Validation panel from the bottom bar to see a live list of issues:
Click Jump โ on any issue to select the relevant node on the canvas instantly.
Timeline continuity checker
The timeline checker validates that items and flags required by conditions can actually be obtained on some path leading to that node.
Overview
Every DYSTEN story is a single JSON object. This reference covers every field, its type, and whether it is required. Use this when hand-editing exported files or building tools that generate story JSON.
Import & Export
Minimum required fields for a valid import:
| Field | Type | Req | Description |
|---|---|---|---|
title | string | โ | Story title โ must be non-empty. |
nodes | array | โ | Array of node objects โ can be empty but must exist. |
state_definitions | array | โ | Array of state definitions โ can be empty. |
status | string | โ | "draft", "published", or "archived". |
All other fields are optional on import โ missing ones receive safe defaults. The id field is always replaced with a fresh generated ID to avoid collisions.
Story Object
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique identifier. Auto-replaced on import. |
title | string | โ | Display name of the story. |
description | string | โ | Short blurb shown on cards and the Discover page. |
status | "draft"|"published"|"archived" | โ | Publication state. |
story_format | "text"|"graphic_novel"|"movie" | โ | Format of the story. Defaults to text. |
genre | GenreType | โ | "heroic_fantasy"|"sci_fi"|"horror"|"mystery"|"modern"|"western"|"custom" |
cover_image_url | string | โ | URL for the cover image shown on cards. |
monetization_mode | "free"|"paid"|"freemium" | โ | Monetization model. |
start_node_id | string | โ | ID of the start node. Must match a node in the nodes array. |
nodes | StoryNode[] | โ | All nodes in the story graph. |
state_definitions | StateDefinition[] | โ | All player state keys. |
gear_definitions | GearItem[] | โ | All items that can exist in the world. |
defaults | StoryDefaults | โ | Starting values for all player state. |
created_at | ISO 8601 | โ | Creation timestamp. |
updated_at | ISO 8601 | โ | Last modification timestamp. |
Nodes
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique node identifier. |
label | string | โ | Human-readable name shown in the editor. |
node_type | NodeType | โ | "scene"|"placeholder"|"ending"|"shop"|"loot" |
content_type | "text"|"image"|"video" | โ | Media type of the scene content. |
content_text | string | โ | Prose text for text content type. |
content_media_url | string | โ | URL for image or video content types. |
placeholder_summary | string | โ | Brief note for placeholder nodes. |
is_start | boolean | โ | True for the starting node only. |
is_ending | boolean | โ | True for ending nodes. |
ending_type | "success"|"failure"|"neutral" | โ | Required when is_ending is true. |
actions | Action[] | โ | Player choices available at this node. |
shop_items | ShopItem[] | โ | Items for sale. Used when node_type is "shop". |
loot_items | string[] | โ | Item keys granted on arrival. Used when node_type is "loot". |
position_x | number | โ | X position on the canvas. |
position_y | number | โ | Y position on the canvas. |
Node Types
Actions & Outcomes
Action
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique action identifier. |
node_id | string | โ | Parent node ID. |
label | string | โ | Button text shown to the player. |
action_template_type | ActionTemplateType | โ | "simple_choice"|"risk_luck"|"skill_check"|"fight_challenge"|"advanced" |
display_order | number | โ | Sort order among actions on the same node. |
is_hidden | boolean | โ | Hidden actions are never presented to the player. |
rule | Rule | โ | How the outcome is selected. |
outcomes | Outcome[] | โ | Possible results. |
Outcome
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique outcome identifier. Also the canvas port handle ID. |
action_id | string | โ | Parent action ID. |
label | string | โ | Port label on canvas (e.g. "Success", "Failure"). |
priority | number | โ | Evaluation order โ lower is checked first. |
min_value | number | โ | Minimum dice roll to match this outcome. |
max_value | number | โ | Maximum dice roll to match this outcome. |
transition | Transition | โ | Target node and state changes. |
Transition
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique transition identifier. |
source_node_id | string | โ | Origin node. |
outcome_id | string | โ | Parent outcome. |
target_node_id | string | โ | Destination node. |
access_type | "free"|"paid" | โ | Paid transitions require a purchase. |
condition_group | ConditionGroup | โ | Gate conditions for this path. |
effect_group | EffectGroup | โ | State mutations applied on take. |
Rule Types
| rule_type | Extra fields | Req | Description |
|---|---|---|---|
none | โ | โ | Simple choice. First outcome with all conditions passing wins. |
probability | dice_definition | โ | Random roll matched to outcome min/max ranges. |
skill_check | skill_key, difficulty_level | โ | d20 + modifier vs. DC. easy=8, medium=12, hard=15, very_hard=18. |
fight | difficulty_level | โ | Combat roll vs. DC. Defeat typically applies health effects. |
custom | custom_expression | โ | Reserved for future scripting. |
Conditions
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique condition ID. |
state_key | string | โ | Player state key or item key to check. |
operator | ConditionOperator | โ | eq | neq | gt | gte | lt | lte | has_item |
expected_value | string|number|boolean | โ | Value to compare against. |
Example
condition_group object"condition_group": { "id": "cg_001", "logic_mode": "all", "conditions": [ { "id": "c1", "state_key": "torch", "operator": "has_item", "expected_value": true }, { "id": "c2", "state_key": "health", "operator": "gte", "expected_value": 10 } ] }
Effects
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique effect ID. |
state_key | string | โ | State key or item key to modify. |
operation | EffectOperation | โ | set | increment | decrement | add_item | remove_item | equip | unequip |
value | string|number|boolean | โ | Value to apply. |
Example
effect_group object"effect_group": { "id": "eg_001", "effects": [ { "id": "e1", "state_key": "health", "operation": "decrement", "value": 20 }, { "id": "e2", "state_key": "gold_key", "operation": "add_item", "value": true } ] }
State Definitions
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique definition ID. |
key | string | โ | Machine-readable key used in conditions and effects. |
state_type | "stat"|"flag"|"inventory"|"counter" | โ | Category of state. |
value_type | "integer"|"boolean"|"string" | โ | Data type. |
default_value | string|number|boolean | โ | Starting value for new sessions. |
visibility_mode | "hidden"|"visible" | โ | Whether the player sees this in the character sheet. |
visual_relevance | boolean | โ | If true, shown prominently (e.g. health bar). |
Gear Definitions
| Field | Type | Req | Description |
|---|---|---|---|
id | string | โ | Unique gear ID. |
key | string | โ | Item key used in loot_items, shop_items, effects, and conditions. |
name | string | โ | Display name shown to the player. |
item_type | "weapon"|"armor"|"consumable"|"key_item"|"misc" | โ | Category for character sheet icon. |
slot | string | โ | Equipment slot (e.g. "weapon", "armor"). Items in the same slot replace each other. |
description | string | โ | Flavour text in shop and inventory panels. |
stat_bonuses | Record<string, number> | โ | Passive bonuses while equipped (e.g. { "strength": 2 }). |
Defaults
| Field | Type | Req | Description |
|---|---|---|---|
starting_stats | Record<string, number> | โ | Initial stat values (e.g. { "health": 100 }). |
starting_flags | Record<string, boolean> | โ | Initial flags (e.g. { "has_map": false }). |
starting_inventory | string[] | โ | Item keys at start (e.g. ["torch", "ration"]). |
starting_counters | Record<string, number> | โ | Initial counters (e.g. { "gold": 50 }). |
starting_equipped | Record<string, string> | โ | Pre-equipped items (e.g. { "weapon": "rusty_dagger" }). |
Full Example
A minimal but complete story โ one scene, one item gate, two outcomes.
{
"id": "story_example",
"title": "The Dark Cave",
"status": "draft",
"story_format": "text",
"genre": "heroic_fantasy",
"monetization_mode": "free",
"start_node_id": "node_001",
"state_definitions": [{
"id": "sd_001", "key": "health",
"state_type": "stat", "value_type": "integer",
"default_value": 100, "visibility_mode": "visible", "visual_relevance": true
}],
"gear_definitions": [
{ "id": "gi_001", "key": "torch", "name": "Torch", "item_type": "consumable" }
],
"defaults": {
"starting_stats": { "health": 100 },
"starting_flags": {},
"starting_inventory": ["torch"],
"starting_counters": {},
"starting_equipped": {}
},
"nodes": [{
"id": "node_001", "label": "Cave Entrance",
"node_type": "scene", "content_type": "text",
"content_text": "You stand at the mouth of a dark cave.",
"is_start": true, "is_ending": false,
"position_x": 200, "position_y": 200,
"actions": [{
"id": "action_001", "node_id": "node_001",
"label": "Enter the cave",
"action_template_type": "simple_choice",
"display_order": 0, "is_hidden": false,
"rule": { "id": "rule_001", "action_id": "action_001", "rule_type": "none" },
"outcomes": [
{
"id": "outcome_001", "action_id": "action_001",
"label": "With torch", "priority": 1,
"transition": {
"id": "trans_001", "source_node_id": "node_001",
"outcome_id": "outcome_001", "target_node_id": "node_002",
"access_type": "free",
"condition_group": {
"id": "cg_001", "logic_mode": "all",
"conditions": [{ "id": "c1", "state_key": "torch", "operator": "has_item", "expected_value": true }]
},
"effect_group": {
"id": "eg_001",
"effects": [{ "id": "e1", "state_key": "torch", "operation": "remove_item", "value": true }]
}
}
},
{
"id": "outcome_002", "action_id": "action_001",
"label": "In darkness", "priority": 2,
"transition": {
"id": "trans_002", "source_node_id": "node_001",
"outcome_id": "outcome_002", "target_node_id": "node_003",
"access_type": "free"
}
}
]
}]
},
{
"id": "node_002", "label": "Cave (Lit)",
"node_type": "placeholder", "content_type": "text",
"placeholder_summary": "Write the lit cave scene here.",
"is_start": false, "is_ending": false, "actions": [],
"position_x": 500, "position_y": 80
},
{
"id": "node_003", "label": "Cave (Dark)",
"node_type": "placeholder", "content_type": "text",
"placeholder_summary": "Write the dark cave scene here.",
"is_start": false, "is_ending": false, "actions": [],
"position_x": 500, "position_y": 320
}]
}