WorldFormat - JakeJamesDev/formamorph GitHub Wiki

๐Ÿ“ World Format

This page describes the .json file of a Formamorph world: the file Export World writes and Import World reads. Use it when you edit a world file by hand or write a tool for one.

๐Ÿ’ก The World Editor writes this format for you. Edit the file by hand only for a change the editor can't make, or for a bulk change.

๐Ÿ“„ This page covers world files only. It does not describe save files.

How to Edit a World File by Hand

  1. In the main menu, select the world. In its world dialog, select Export World.
  2. Open the .json file in a text editor. Make your changes.
  3. In the main menu, open the Worlds tab and select Import World. Pick the file.

๐Ÿ’ก The World Editor's Overview tab has Export World too.

The import adds a new world. It never replaces the world you exported. Import skips a file that fails to load, and the menu tells you.

How to Add a Stat

  1. Add an object to the top-level stats list.
  2. Give it a new id and a name. Scripts and the AI find a stat by its name, so keep names unique.
  3. Set type to "number", or to "percentage" for a 0โ€“100 stat.
  4. Set min, max, value (the Base Value) and regen.
  5. Write a description, and set descriptors to [] or to a list of descriptors.
{ "id": "stamina", "name": "Stamina", "type": "number", "description": "Energy for hard work",
  "min": 0, "max": 100, "value": 80, "regen": 2, "descriptors": [] }

How to Add a Trait

  1. Add an object to the top-level traits list. For a trait that one entity owns, add it to that entity's traits list.
  2. Give it a new id and a name. Trait ids must be unique across the world and every entity.
  3. Write playerDescription and aiDescription.
  4. Set statChanges to [] or to a list of stat changes.
  5. To put it in a group, set groupId to the group's id.
{ "id": "strong", "name": "Strong", "playerDescription": "Above-average strength",
  "aiDescription": "This person is unusually strong.", "groupId": "physical",
  "statChanges": [{ "statId": "stamina", "value": 10, "type": "max" }] }

How to Add an Entity

  1. Add an object to the top-level entities list.
  2. Give it a new id and a name.
  3. Write playerDescription, aiDescription and, for a long description, aiSummary.
  4. Set locations to the ids of the locations where the entity is. Leave it out for an entity with no fixed place.
  5. To make the entity playable, set persona to true. See Persona Marks.
{ "id": "smith", "name": "Mara", "aliases": ["the smith"], "pronouns": "she/her",
  "playerDescription": "The village blacksmith.", "aiDescription": "Mara is a gruff, fair blacksmith.",
  "locations": ["town"] }

Reading the Tables

Mark Meaning
โœ“ in Req. The world types always carry this field. Write it.
No mark Optional. The description says what an absent field means.
id A unique string. The editor writes random UUIDs. Any unique string works.

Editor-only fields organize the World Editor. The AI never reads them.

The File

Field Req. Type Meaning
formamorphKind "world" Export writes it. Import does not need it.
version String The app version that wrote the file, such as "3.1.2". See Versions and Older Files
worldOverview โœ“ Object Name, listing text and world-wide settings. See worldOverview
stats โœ“ Stat[] The player's numbers
locations โœ“ Location[] Places. A location can nest inside another
connections Connection[] Travel links between locations. Absent = travel follows nesting only
entities โœ“ Entity[] People, creatures and objects
entityGroups Group[] Editor-only groups for entities
traits โœ“ Trait[] The world's traits
traitGroups TraitGroup[] Groups for the world's traits
placeholders Placeholder[] The world's shared placeholders
placeholderGroups Group[] Editor-only groups for shared placeholders
dictionaries โœ“ Dictionary[] Lore books. Import makes one empty book when the list is absent or empty
statUpdates โœ“ StatUpdate[] An older field. Write []
scripts Script[] Scripts no stat owns. They run with no self

A stored world also has an id. Export leaves it out, and import gives the world a new one.

โš ๏ธ Import refuses a world without worldOverview, stats, locations, entities, traits and statUpdates. An empty list is enough.

Versions and Older Files

Import runs every world through a migration. The migration changes an older shape into the current one, so a file from any version loads. These older forms still load:

Older form Loads as
No version (before 2.0) The current shape
A flat dictionary list One book named "Default" in dictionaries
A dictionary key or secondaryKeys as one comma-separated string A list of keywords
An entity image string The first item of images
An entity images list of strings Each string as { "data": โ€ฆ }
A location entities list Each entity's locations
A location connections list of names Records in the top-level connections
A connection with from, to, twoWay and aiHint A connection with a, b and legs
A location isStartLocation flag isStarting
Placeholder values as strings, weights keyed by text Value records, weights keyed by value id
worldOverview.openingCue and openingCueEnabled One Opening Action in openings, and openingsEnabled
worldOverview.playerSetting allowedPersonas and startPersona
A trait group exclusive: true maxPicks: 1
An entity link to a trait outside Blueprints Removed, with its overrides
A stat's beforeCode and code Scripts with the timings "before" and "after"
A stat with type: "list" A number stat at its min. The items are dropped
A script that uses stats.find(โ€ฆ) The same code with the stat map
A Stomach, Fatness or Breastsize stat with no morphBindings The matching body morphs
A root customPlayerVRM worldOverview.customPlayerVRM
Entity and location inGameDescription and detailedDescription playerDescription and aiDescription
A trait description Both playerDescription and aiDescription

โš ๏ธ The last six rows run only when version is not the app's version. A file stamped with the current version keeps those older forms as written.

Write new files in the current shape. A file with scripts loads in an older version of the app, but its scripts don't run there.

worldOverview

Field Req. Type Meaning
name โœ“ String The world's name
description โœ“ String The Player-Facing Description: card and listing text. The AI never reads it
author โœ“ String Free text that names the author
thumbnail โœ“ Image | null The card image
bgm โœ“ Audio | null Background music, as a data URL
systemPrompt โœ“ String The AI-Facing Description: what the AI reads about the world. Players never see it
use3DModel โœ“ Boolean The world shows a 3D avatar
tags โœ“ String[] Listing tags
customPlayerVRM MediaAsset | null The world's own player avatar, a .vrm file
readme String Markdown the player sees when play starts
introReadme String Markdown the player sees on the first setup screen. It shares the readme's on and off switch
promptOverrides Object The world's Custom Prompts. See below. Absent = the player's prompts
openings Opening[] The world's own openings, in order
openingWeights Object Draw weight per opening id. A missing entry weighs 1. 0 keeps an opening and never draws it
openingsEnabled Boolean false stops every opening of the world, its locations and its entities from drawing. Absent = on
allowedPersonas "any" | "world" Which personas the player can pick. "world" allows only this world's personas and its Custom Persona. Absent = "any"
startPersona Object The persona a new player starts on: { "source": "none" }, or { "source": "world", "entityId": "<entity id>" }. Absent = the player's default persona

See Persona Rules for how the last two fields behave.

promptOverrides replaces some of the player's prompts while they play this world:

Field Type Meaning
systemPrompt String Replaces the narration system prompt
systemPromptEnabled Boolean false keeps the text and does not use it. Absent = used
choicesPrompt String Replaces the choices system prompt
choicesPromptEnabled Boolean false keeps the text and does not use it
statUpdatesPrompt String Replaces the stat updates system prompt
statUpdatesPromptEnabled Boolean false keeps the text and does not use it

Stats

Each stat is a number the player has. The AI reads it, and a script can calculate it.

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The name. Scripts and the AI find the stat by it
type โœ“ "number" | "percentage" A percentage stat has min 0 and max 100, and shows as N%. The AI can't change its max
description โœ“ String What the stat means
min โœ“ Number The lowest value
max โœ“ Number The highest value
value Number The Base Value: what a new game starts at, before traits apply. Absent or 0 = min
starting Number The value a save gets when it did not have this stat yet. A new game also measures turn one's change from it. Absent = value. Keep it equal to value
regen โœ“ Number The change per story hour. Negative drains
regenUnit "minutes" | "hours" | "days" | "weeks" The span the editor shows Regen per. regen stays per hour. Absent = "hours"
descriptors โœ“ Descriptor[] Text for each band of values
thresholdUnit "raw" | "percent" What a descriptor's threshold measures. "raw" = the stat's own units. "percent" = a percent of min to max. Absent = "raw". A percentage stat is always percent
scripts Script[] Code with this stat as self. See the Scripts Guide
morphBindings String[] Body morph names this stat drives. min to max maps to 0 to 1
enabled Boolean false starts the stat off. The player and the AI don't see it, and regen and code stop. A trait's statToggles switches it on. Absent = on
hidden Boolean true hides the stat from the player. The AI still reads it, and regen and code still run
noIncrease / noDecrease Boolean The AI can't raise or lower the value
noIncreaseMax / noDecreaseMax Boolean The AI can't raise or lower the max

Scripts

A script is one box of code with a timing. A stat, a trait and the world each hold a scripts list.

Field Req. Type Meaning
id โœ“ String Unique id
name String The script's name. Absent = the owner and timing name it
timing โœ“ "before" | "after" | "output" | "start" | "switch" When it runs. "before" runs before the AI's turn, and its changes reach that turn's prompt. "after" runs after the AI's changes and regen. "output" runs on each completed narration sentence. "start" runs once on a new game's opening turn. "switch" runs when a trait turns on or off
code โœ“ String The JavaScript
order Number Run position within the timing. Scripts with an order run first, lowest first. The rest run after them, world scripts first, then each stat's in stat order

When two scripts write the same field, the later one wins. A world script has no self. A trait's script runs only while the trait is active. The Scripts Guide lists what each timing reads.

Stat Descriptors

A descriptor gives the AI a word or phrase for a band of values.

Field Req. Type Meaning
id โœ“ String | Number Unique id
threshold โœ“ Number The top of the band, in the stat's thresholdUnit
description โœ“ String The text the AI reads while the value is in the band
placeholderPins Pin[] Placeholder values pinned while the value is in the band

The lowest band that holds the value wins, in any list order. A value above every threshold has no descriptor.

Stat Changes

A trait changes a stat with a list of these.

Field Req. Type Meaning
statId โœ“ String The stat's id
value โœ“ Number The amount to add. Negative subtracts
type "min" | "max" | "starting" | "regen" What the change adds to. "starting" adds to the value. Absent = no change
interval String An older field. Nothing reads it
target "global" On an entity's trait, "global" moves the global stat with this id, even when the entity has its own stat with the same id. Absent = the entity's own stat first, else the global stat

A statToggles entry takes the same target.

๐Ÿ” Older files write "target": "player". The app reads it as "global", and new files write "global" only. Apps older than this change don't read "global", so they apply these rows to the entity's own stat until updated.

Traits

A trait describes the player or an entity. The player picks traits before play. An entity can own traits too.

Field Req. Type Meaning
id โœ“ String Unique across the world and every entity
name โœ“ String The name
playerDescription String What the player reads on the setup screen
aiDescription String What the AI reads while the trait is active
statChanges โœ“ StatChange[] Changes to stats while the trait is active. A global trait changes global stats. An entity's trait changes the entity's own stats, and global stats through a "global" row
groupId String | null The group's id. Absent or null = the top level
order Number The order among items in the same group
isDefault Boolean The trait starts selected
playerToggle Boolean The player can switch the trait on and off during the game
mode "alwaysOn" | "hidden" Absent = Optional: the player picks. "alwaysOn" is active while its requirements hold, and the player can't switch it. "hidden" acts as "alwaysOn", and the player never sees it. Both ignore isDefault and playerToggle
requires Requirement row[] Any one row that holds makes the trait available. Absent or empty = always available
statToggles { "statId", "enabled" }[] Stats switched on or off while the trait is active
placeholderPins Pin[] Placeholder values pinned while the trait is active
scripts Script[] Code with this trait as self. It runs while the trait is active. See the Scripts Guide

See World Editor: Traits for how modes, requirements and pick counts behave during the game.

Trait Requirements

requires is a list of rows. Each row is { "all": [ โ€ฆ ] }: a list of requirements. A row holds when every requirement in it holds. A row is never empty.

"requires": [
  { "all": [{ "kind": "trait", "id": "knight" }, { "kind": "trait", "id": "heavy-build" }] },
  { "all": [{ "kind": "trait", "id": "mercenary" }] }
]

๐Ÿ” Older files store requires as a flat list of requirements. The app reads each one as a row of its own.

Each requirement has a kind:

kind Other fields Holds when
"trait" id, name, owner The owner has the trait with this id
"group" id, name, owner The owner has any trait in the group with this id
"playingAs" id, name The player plays as the entity with this id
"stat" id, name, owner, operator, value, unit The owner's stat with this id compares true against value
  • name is optional. It holds the target's name, so a requirement still reads when the target is gone.
  • A "stat" requirement's operator is one of ">", "<", ">=", "<=", "=" or "โ‰ ". Its unit is "raw" for the stat's own units or "percent" for a share of the stat's minโ†’max span. { "kind": "stat", "id": "health", "operator": "<", "value": 20, "unit": "percent" } holds while Health is under 20% of its range.
  • owner is optional, and it applies only to a trait, group or stat an entity owns. A requirement that names a global item (a root trait, a root group or a global stat) reads the global scope, and the app drops any owner stored with it. Absent = the trait's own owner. { "kind": "you" } checks the entity the player plays, and never holds when the player plays none. { "kind": "entity", "id": "<entity id>", "name": "โ€ฆ" } checks that entity.
  • not is optional. true flips the requirement: it holds while its target is off. { "kind": "trait", "id": "paladin", "not": true } holds while the owner doesn't have Paladin.
  • A requirement whose target is gone never holds, with or without not.

๐Ÿ” Older files name the owner under bearer. The app reads it as owner, and new files write owner only. Apps from v3.1 and v3.2 don't read owner, so they misread these requirements until updated.

Trait Groups

A group holds traits and other groups. Both the world and each entity have a traitGroups list.

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The heading on the setup screen
playerDescription String Text the player reads under the heading
aiDescription String A header the AI reads above the group's active traits
aiVisible Boolean false keeps the group's name and description from the AI. Its traits and subgroups move up one level. Absent = true
parentId โœ“ String | null The parent group's id. null = the top level
order Number The order among items with the same parent
minPicks Number The fewest traits placed directly in the group that must be active. Absent = 0
maxPicks Number The most traits placed directly in the group that can be active. Absent = no limit. 1 shows radio buttons
system "blueprints" Marks the world's Blueprints group. Only one world group has it, at the top level

Blueprints

The world group with system: "blueprints", and every group below it, holds Blueprints items. The player never picks a Blueprints trait. It reaches play only through an entity's link. Only a Blueprints trait or group can be linked.

Locations

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The name
playerDescription String What the player reads
aiDescription String The full description the AI reads
aiSummary String A short description the AI reads where the full one is too long
description String An older field. The player reads it when playerDescription is absent
backgroundImage Image The background
imageTags String Comma-separated tags for image generation
ambientSound MediaAsset Looping sound
isStarting Boolean A new game can start here. One marked = every game starts there. Several = the player picks. None = any location. A played entity's startingLocationId joins the choices
parentId String | null The parent location's id. Absent or null = the top level. List order is the order among siblings
canvasPosition { "x", "y" } Editor-only. The location's place on the Locations canvas, relative to its parent. Absent = automatic
placeholderPins Pin[] Placeholder values pinned while the player is here. A child location does not get its parent's pins
openings Opening[] Openings that draw when a game starts at this exact location
openingWeights Object Draw weight per opening id, as in worldOverview

Connections

A connection is a travel link between two locations. A connection between two locations replaces the travel that nesting gives them. Its legs are then the only travel between the two.

Field Req. Type Meaning
id โœ“ String Unique id
a โœ“ String One location's id
b โœ“ String The other location's id
aToB Leg Present when travel goes from a to b
bToA Leg Present when travel goes from b to a

A connection needs at least one leg. Both legs = two-way. A leg has one optional field, hint: how the trip is made, such as "through the old gate". The AI reads it after the destination's name.

Entities

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The name
aliases String[] Other names. The AI reads them. The app finds them in the story text, case-sensitive and whole-word
pronouns String Free text, such as "she/her". The AI reads it
type String An optional category label
persona Boolean The Persona mark. See Persona Marks
personaOnly Boolean The entity exists only while the player plays it
customPersona Boolean The Custom Persona mark
startingLocationId String The Starting Location preselected when the player plays this entity. Absent = Automatic: the first of its locations marked isStarting
playerDescription String What the player reads
aiDescription String The full description the AI reads
aiSummary String A short description the AI reads where the full one is too long
images Gallery image[] The gallery, in author order. The first image is the main one
tags String[] Listing tags for Community Creations
imageTags String Comma-separated tags for image generation
sound MediaAsset A sound
model MediaAsset A 3D model
locations String[] The ids of the locations where the entity is. It is at all of them
groupId String | null Editor-only. The entity group's id. Absent or null = no group
order Number Editor-only. The order in its group
openings Opening[] The entity's own openings
openingWeights Object Draw weight per opening id, as in worldOverview
placeholders Placeholder[] Placeholders the entity owns, and its copies
traits Trait[] Traits the entity owns. They describe it to the AI. They are the player's when the player plays it
traitGroups TraitGroup[] Groups for the entity's own traits
traitLinks TraitLink[] Links to Blueprints traits and groups
traitPlacement { "groupId", "order" } Where the entity's node sits in the world's Traits tree. groupId is a world group id or null for the top level. Absent = the end of the top level
link ContentLink What library item this copy follows. Absent = it follows nothing

Only the player has stats. So statChanges and statToggles on an entity's own traits apply only on a playable, persona-only or Custom Persona entity. A playable or persona-only entity's apply while the player plays it. The Custom Persona entity's apply while the player has no world persona.

Persona Marks

The Persona control on the entity's Profile tab writes three fields:

Choice persona personaOnly customPersona
Cast absent absent absent
Playable true absent absent
Persona-Only true true absent
Custom Persona absent absent true

One entity at most has customPersona, at the top level of the Traits tree. In a library entity, persona: true makes it one of the player's personas. See Personas for Authors for the rules.

Trait Links

A link gives a Blueprints trait or group to an entity. The entity does not get its own trait. The link reads its original live, except for the fields it overrides.

Field Req. Type Meaning
id โœ“ String Unique id
originalId โœ“ String The id of the Blueprints trait or group
kind โœ“ "trait" | "group" What the original is
originalName โœ“ String The original's name. When an entity joins a world, its link finds the original by originalId, then by this name
groupId โœ“ String | null One of the entity's own group ids. null = the entity's top level
order Number The order among the entity's items with the same parent
overrides Object Overrides per trait. The key is the original trait's id. A linked group keys each of its traits here
keyNames Object Outside a world only, such as in an entity file: trait id to name, for each key of overrides

Each override map can hold isDefault, requires, placeholderPins, playerToggle, statChanges and mode. mode takes "optional", "alwaysOn" or "hidden". Each field holds { "value": โ€ฆ, "blueprint": โ€ฆ }: your value, and the original's value when you set it. The app uses blueprint to tell you when the original changed.

An entity links each original one time.

Entity Groups

Editor-only groups for entities.

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The group name
parentId โœ“ String | null The parent group's id. null = the top level
order Number The order among groups with the same parent

Entity Files

An entity file, or an entity in the library, can also carry these fields. The app clears them when the entity joins a world, so a world file does not need them.

Field Type Meaning
sharedPlaceholders Placeholder[] The world placeholders the entity's chips use
blueprints Placeholder[] The blueprints the entity's copies read
locationRefs { "id", "name" }[] The locations the entity stood in, by name

Openings

An opening is one way a game can start. The world, each location and each entity have an openings list.

Field Req. Type Meaning
id โœ“ String Unique within its owner. openingWeights uses it as the key
text โœ“ String The text. It can hold chips
kind โœ“ "action" | "narration" "action" fills the player's input box. "narration" is page one, as written
self true A Self opening. It draws only while the player plays its owner. Only an entity with the Persona or Custom Persona mark can own one. Absent = an Others opening

See World Editor: Openings for which openings draw.

Placeholders

A placeholder is a named value that a chip shows in text. The world's placeholders are shared. An entity's or a book's placeholders belong to it.

Field Req. Type Meaning
id โœ“ String Unique id. Do not use :, { or } in it
name โœ“ String The name in the editor. Chips use the id, so a new name breaks nothing
values โœ“ Value[] The values. See below. Empty = the chip reads as nothing
weights Object Draw weight per value id. A missing entry weighs 1. 0 keeps a value and never draws it
roll Boolean true = a Wildcard, which draws one value per game. false = an Object, which joins all values with ", ". Absent = a Wildcard with two or more values. With one value, the placeholder is a Variable
ownerId String The placeholder this one belongs to. Absent = the top level
sharedWeights Object Draw weights this placeholder sets on shared placeholders it reaches through its values. The outer key is a path of ids joined with /. The inner map is like weights
blueprintId String Makes this a copy of that blueprint
valueOverrides Object On a copy: changes per blueprint value id
groupId String | null Editor-only. A shared placeholder's group id. Absent or null = no group

Each value:

Field Req. Type Meaning
id โœ“ String Unique id. Pins and weights use it
text โœ“ String The text. It can hold chips. A value that is exactly one chip makes that placeholder a part of this one
pins Pin[] Placeholder values pinned while this value is the drawn one

See World Editor: Placeholders for kinds, parts and weights.

Placeholder Pins

A pin sets one placeholder to one value while its source is active. Traits, locations, stat descriptors and placeholder values carry pins.

Field Req. Type Meaning
placeholderId โœ“ String The placeholder's id. On a trait, a blueprint id pins each owner's copy
value โœ“ String The value's text
valueId String The value's id. When present, it wins over value, so a reworded value still matches

Copies

A world shared placeholder in the Blueprints group, or a group below it, is a blueprint. An entity holds a copy of each blueprint its traits need, in its own placeholders, with blueprintId set. The app makes copies when it loads the world.

A copy reads its blueprint's values live. Its own values hold only the values it adds. Its valueOverrides map a blueprint value id to changes:

Field Type Meaning
text { "value", "blueprint" } Your text for the value, and the blueprint's text when you set it
weight { "value", "blueprint" } Your draw weight, and the blueprint's weight when you set it
removed true This copy never draws the value

Placeholder Groups

Editor-only groups for the world's shared placeholders.

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The group name
parentId โœ“ String | null The parent group's id. null = the top level
order Number The order among groups with the same parent
system "blueprints" Marks the Blueprints group. Only one group has it, at the top level

Chips in Text

A chip is a token in a text field. The editor inserts chips. To write one by hand, use this form:

{{ph:<placeholder id>:<world|unique>:<placement id>}}
Part Meaning
<placeholder id> The placeholder's id
world Every chip of this placeholder shows the same drawn value in a game
unique This chip draws its own value
<placement id> A unique id for this chip. A unique chip keeps its draw under it

The editor can add two more parts after the placement id: a path to a part of the placeholder, and a label after :=. Copy those from a chip the editor wrote.

Two chips need no placeholder. {{user}} is the Player Name chip. {{char}} is the Character Name chip.

Dictionaries

dictionaries is a list of books. Each book holds lore entries. An entry goes into the AI's prompt when one of its keywords is in the scanned text. See What Gets Scanned.

Each book:

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String The book's name
description String A note for people. The AI never reads it
enabled Boolean false turns off every entry in the book. Absent = on
tags String[] Listing tags for Community Creations
thumbnail Image | null The listing image
entries โœ“ Entry[] The entries, in order
placeholders Placeholder[] Placeholders the book owns
sharedPlaceholders Placeholder[] In a dictionary file only: the world placeholders its entries use. Import clears it
link ContentLink What library item this copy follows

Book order sets the order in the prompt.

Dictionary Entries

Field Req. Type Meaning
id โœ“ String Unique id
name โœ“ String A label. Blank = the first keyword
key โœ“ String[] The keywords, one per item
value โœ“ String The text the AI reads when the entry is active
enabled Boolean false turns the entry off. Absent = on
constant Boolean The entry is always active
secondaryKeys String[] Second keywords. When present, a keyword match also needs a second keyword
secondaryAll Boolean Needs all second keywords, not one
secondaryExclude Boolean Inverts the second test: the entry is active only when the second keywords are absent
useRegex Boolean Keywords are regular expressions
matchWholeWords Boolean Keywords match whole words only, regex keywords included
caseSensitive Boolean Keywords match case. Absent = any case
recursive Boolean Text from other active entries can activate this entry
position "before" | "after" "before" = Background Lore, early in the prompt. "after" = Foreground Lore, late. Absent = "after"
scanDepth Number Scan only the last N messages. The app always scans the current scene. Absent = all
priority Number Kept from an imported lorebook for export. The app does not use it
tokenBudget Number Kept from an imported lorebook for export. The app does not use it
extensions Object Other imported lorebook fields, kept for export

Linked Copies

An entity or a book in a world can follow a library item. Its link object records that. Let the app write it. See Linked Content.

Field Type Meaning
libraryId String The library item's id
sourceId String The published listing behind the library item
sourceRevision String The source revision this copy holds
reviewedRevision String The last revision the author reviewed
localReplacement Boolean The copy was edited and still follows its source
sourceName String The source's name, for display
connections Object What each world placeholder or location the source expects is in this world, by id
bundledFrom String The copy came in an imported world file, and its library item is not on this machine

Stat Updates

statUpdates is an older list. No screen edits it, and play does not read it. Write []. An older file can hold records with id, name, prompt, stats (stat names) and messageHistory.

Media Fields

Images (thumbnail, location backgroundImage, a gallery image's data, book thumbnail) hold one string:

  • A base64 data URL, such as data:image/png;base64,โ€ฆ
  • Or an http or https URL. The app loads the image from that address

Gallery images (entity images) are objects:

Field Req. Type Meaning
data โœ“ Image The picture
requires Requirement rows The Visibility rule. Absent = always shown

Background music (bgm) is a base64 data URL.

Uploaded audio and models (customPlayerVRM, location ambientSound, entity sound and model) are a MediaAsset:

Field Req. Type Meaning
data โœ“ String A base64 data URL
type โœ“ String The MIME type, such as model/vrm
name String The original file name
size Number The file size in bytes
{ "data": "data:model/vrm;base64,...", "type": "model/vrm", "name": "hero.vrm" }

Example World

A short world. ... replaces the media data.

{
  "formamorphKind": "world",
  "version": "3.1.2",
  "worldOverview": {
    "name": "Example World",
    "description": "A small town at the edge of the woods.",
    "author": "Docs",
    "thumbnail": "data:image/png;base64,...",
    "bgm": null,
    "systemPrompt": "A quiet fantasy town. Magic is rare and feared.",
    "use3DModel": false,
    "tags": ["fantasy"],
    "readme": "# Welcome\nYou arrive at dusk.",
    "openings": [
      { "id": "op-1", "text": "{{user}} reaches the town gate as the bell rings.", "kind": "narration" }
    ]
  },
  "stats": [
    {
      "id": "health",
      "name": "Health",
      "type": "number",
      "description": "How hurt you are",
      "min": 0,
      "max": 100,
      "value": 100,
      "regen": 1,
      "descriptors": [
        { "id": "hp-low", "threshold": 30, "description": "badly hurt" },
        { "id": "hp-high", "threshold": 100, "description": "healthy" }
      ]
    }
  ],
  "locations": [
    { "id": "town", "name": "Town Square", "playerDescription": "The center of town.", "isStarting": true },
    { "id": "forge", "name": "Forge", "playerDescription": "Heat and noise.", "parentId": "town" }
  ],
  "connections": [],
  "entities": [
    {
      "id": "smith",
      "name": "Mara",
      "pronouns": "she/her",
      "playerDescription": "The town blacksmith.",
      "aiDescription": "Mara is gruff and fair. She wears a {{ph:garb:world:p-1}}.",
      "locations": ["forge"]
    }
  ],
  "traits": [
    {
      "id": "strong",
      "name": "Strong",
      "playerDescription": "Above-average strength",
      "aiDescription": "This person is unusually strong.",
      "groupId": "body",
      "statChanges": [{ "statId": "health", "value": 20, "type": "max" }]
    }
  ],
  "traitGroups": [
    { "id": "body", "name": "Body", "parentId": null, "order": 0, "maxPicks": 1 }
  ],
  "placeholders": [
    {
      "id": "garb",
      "name": "Garb",
      "values": [
        { "id": "v-1", "text": "leather apron" },
        { "id": "v-2", "text": "soot-black smock" }
      ]
    }
  ],
  "dictionaries": [
    {
      "id": "book-1",
      "name": "Default",
      "enabled": true,
      "entries": [
        { "id": "lore-1", "name": "The War", "key": ["war", "old war"], "value": "A war ended here 40 years ago." }
      ]
    }
  ],
  "statUpdates": []
}

Related

โš ๏ธ **GitHub.com Fallback** โš ๏ธ