Skip to content

Your First Fragment

We’ll build a complete fragment step by step.

One scene from a life. A character reaches a moment that matters (or doesn’t), is offered a few choices, and lives with the one they take. That is the whole unit.

A life is assembled by picking fragments one after another, so yours will most likely be read between two scenes you have never seen, written by people you have never met. Three things follow from that:

  • It is short. A few paragraphs and a handful of choices. A scene, not a chapter.
  • It stands alone. You cannot know what came before it, so nothing in it may assume. You can check what happened, with stats, variables, or whether a particular fragment was played, but you can never take it for granted.
  • It does not resolve the life. The player carries what happened out of your scene and into somebody else’s. Unless the character dies, of course.

Every fragment is its own file, named after the fragment’s id and ending in .frag. The one we’re about to write is called youth_first_job, so create youth_first_job.frag.

Every fragment begins with === and a unique snake_case id, followed by header fields:

=== youth_first_job
phase: youth
title: The First Summer Job
tags: career, independence, money
requires: WEA <= 60
  • phase: says when in life this scene can occur (childhood, youth, early-adulthood, middle-life, maturity, late-life).
  • title: is shown to the player.
  • tags: are comma-separated; the first one matters most - the game avoids picking three fragments with the same first tag in a row.
  • requires: decides which characters can get this fragment at all. It is optional: without one, anybody in the right phase is a candidate.

A blank line ends the header.

A character is eligible for a fragment when three things are true: they are in its phase:, they have not already played it in this life, and its requires: condition passes. The game then picks from what is left, so requires: is where you say who this scene is for. Our summer job is for someone who needs the money, which is what WEA <= 60 says.

It takes one condition, in the same grammar used everywhere else in the format. && is and, || is or, ! is not, and parentheses group however you like:

requires: WEA <= 60 && age >= 15

Or, with an alternative in it:

requires: (WEA <= 60 || REP <= 40) && !friend_kevin

That one asks for a character who is either short of money or short of standing, and who has not got Kevin for a friend either way. Its last term reads a variable, and a variable has to be declared before a fragment may touch it, which is Variables at the bottom. Stats and age need no such thing.

The terms are stats, age, variables, and played: for “has lived through that other fragment”. The full grammar is in Conditions, and played: in particular is how a fragment becomes a sequel to somebody else’s, which Contributing Workflow covers.

Two things to hold on to. Ask for what the scene needs and no more, because every condition you add is a set of characters who will never see it, and a fragment gated tightly enough is one nobody meets. And this gates the whole fragment: to gate a single choice inside it, put *requires: on that choice instead, which is the next section but one.

Whatever follows the header is the scene the player reads:

The summer before your last year of school. The new phone isn't going
to buy itself. So on a Tuesday morning you find yourself outside the
ice cream shop, asking the owner for work.

Blank lines separate paragraphs. You can personalize text with inline tokens - {name}, {they}, {their} - and branch it on stats:

He looks {if CHA >= 60}you over with something like approval{else}right through you{endif}.

Choices start with > . Offer as many as the situation invites; a handful is typical:

> Smile broadly and pour on the charm
*requires: CHA >= 65 #Can only be selected with a charismatic character
*effects: WEA+3, CHA+1, HAP+2
He smiles back and holds out his hand. You've got the job.
> Distract him and steal from the register
*requires_hidden: MOR <= 30 #Only visible to an immoral character
-> theft
> Walk away
*effects: HAP+2, WEA-2
You spend the summer at the lake instead.

Everything under a choice (until the next >) is its outcome:

  • *requires: gates the choice - unmet, it shows disabled with a hint.
  • *requires_hidden: hides the choice entirely if the conditions aren’t met.
  • *effects: moves stats. The number is a percentage of the room a stat has left, so +10 on a middling stat is about five points. Sizing a number puts the range beside the kinds of thing that happen to people, which is the easier way to pick one.
  • The rest is outcome prose.

A choice can also remember what happened, so a later scene can know about it. That needs a variable, and a variable has to be declared before you can touch it, so it waits for Variables at the bottom of this page.

Effects fire where you place them, not when the choice is clicked. A ... line pauses the reveal until the player presses Continue:

> Lend Tommy your bike
"Back in an hour," he says.
...
A week later you spot it, rusting, in the creek.
*effects: HAP-3, REP-1
You never lend him anything again.

The player picks the choice, reads the first line, presses Continue - and only then learns what it cost them. Real life doesn’t show you the stat change up front either.

Use *if / *elseif / *else / *endif blocks to vary prose and effects by state:

> Ask about the hourly rate
*if CHA >= 70
After some back-and-forth you settle on seven an hour plus tips.
*effects: WEA+4, INT+1
*elseif CHA >= 50
Six, he says, without looking up. You ask what happens to the tips. He
shrugs, and you decide the shrug means they are yours.
*effects: WEA+3
*else
His eyebrows climb. "Six. Take it or leave it." You take it.
*effects: WEA+2
*endif

Add as many *elseif arms as the situation has outcomes. The first one whose condition is true wins and the rest are skipped, so they go from the hardest to reach down to the easiest: were CHA >= 40 written first, nobody would ever see the good outcome. *else is optional, and catches whatever is left.

Where *if branches on what is true, *random branches on luck. Exactly one arm fires, picked by weight:

> Ask whether he is hiring at all
*random
*chance 70
"Not today." The door closes before you finish the sentence.
*chance 30
He shrugs. "One of my lads quit this morning. Can you start now?"
*effects: WEA+3
*endrandom

Weights are relative, so 70 and 30 read as percentages while 7 and 3 do the same job.

6. Going deeper: nested choices and subscenes

Section titled “6. Going deeper: nested choices and subscenes”

A choice can lead to more choices - either nested with a deeper marker:

> Ask the owner about the job
"Depends," he says. "Mornings or evenings?"
>> Mornings
*effects: VIT+1, HAP-1
You'll have to get used to getting up at 5am.
>> Evenings
*effects: CHA+1
You learn every regular's order by heart.

…or in a subscene you jump to with -> (the jump is seamless - the player reads straight through into the next scene):

> Take the job on the spot
-> first_shift
@ first_shift
The apron doesn't fit. The register doesn't follow your commands.
> Focus on the customers
*effects: CHA+2
> Focus on the machine
*effects: INT+2

Subscenes can jump back to themselves to make hubs - already-taken choices come back marked “Taken” on a revisit (mark a choice >* to keep it repeatable). See Hubs for the visit-counter idiom and the two footguns the build warns about.

You do not install anything to find out whether your fragment works. /submit on the Discord posts the file to a private thread, where a bot parses it and reports back, usually within a minute: either that it is sound, or the exact line that stopped it.

Fixing it is the same loop. Drop the corrected file into that same thread and it is checked again, as many times as you like. Nothing is public while you do it.

The check is about format, not quality. Passing means the file works, not that the fragment is good; maintainers will read it after that and either accept it or send it back to you with notes. The full route, from registering a handle to being credited in the game, is in Contributing Workflow.

Every message the bot can give you is in Build Errors Explained, with what caused it and how to fix it.

Everything above makes one scene work on its own. These three are how a scene stops being alone: what it remembers, what it inherits, and who is in it.

Stats are a blunt memory. A variable is a specific one: this happened, and a scene years later can know it.

Every variable a fragment touches is declared in that fragment, in one or both of two blocks that sit between the header and the prose:

=== youth_first_job
phase: youth
title: The First Summer Job
tags: career, independence, money
*use
lies_told
*enduse
*declare
local flag told_him_straight
local count shifts_worked
*enddeclare
  • *declare introduces a variable. Mark it local and it belongs to your fragment alone, stored behind the scenes as youth_first_job.shifts_worked, so your counter and somebody else’s cannot collide and you can name it whatever reads best.
  • *use names shared state that already exists, like lies_told. Every shared name is recorded once, with a sentence settling what it means: lies_told counts deliberate untruths somebody believed at the time, and not silences, evasions, or jokes nobody took seriously. That sentence is the whole point of the registry, because it is the question the type cannot answer.
  • Three types. A flag is true or false. A count is a whole number, and the only thing += and >= accept. An enum is one of a fixed set you list.

Declaring is not paperwork. A variable nobody set reads as 0, so a misspelled name is not an error, it is a branch that silently never fires and a scene no player ever sees. Declaring turns that silence into a build error with your typo in it.

Now the choices can remember:

> Tell him straight that you need the money
*set: told_him_straight
He nods slowly. "I appreciate your honesty"
> Say you have three other offers
*set: lies_told += 1
He eyes you suspiciously.

And anything that declared the same names can read them, here or in a fragment written by somebody else years later:

*if told_him_straight
He still remembers what you said that first morning.
*endif

*if lies_told >= 3 compares a count, *if !told_him_straight is the negative, and *if told_him_straight = false says the same thing in the spelling *set: uses. *set: x = false takes a fact back and keeps it as an undone one, while *unset: x erases it as though it never happened. Full grammar in Variables.

Some facts about a character are not made by a scene, only found out in one: how a body takes to sugar or to drink, which hand it writes with. Declare those innate and they are settled once, when the life begins, for every life, whether or not your fragment ever plays:

*declare
innate count sweet_tooth = random(6)
*enddeclare

random(N) spreads a count over 1 to N. chance(N) makes a flag true in N lives out of a hundred. A constant gives everyone the same start. Your fragment then only reads it:

*if sweet_tooth >= 5
You have eaten more of the stock than you have sold.
*endif

The point is that nobody rolls it. Were the trait rolled where it is first read, whichever fragment happened to run first would own a roll that is not about it, and every other fragment touching the trait would have to carry the same “roll it if nobody has yet” guard forever.

A variable without local is shared, and shared state is a proposal rather than a decision: mention it in your submission thread and a maintainer records what it means. The build warns until they do.

The same two blocks declare who is in the scene and where it happens, with a kind word in front of the name:

*use
character kevin
*enduse
*declare
character mr_petrakis { name: "Mr. Petrakis", description: "runs the ice cream shop, and has run it a long time" }
place the_shop { description: "the ice cream shop on the corner" }
*enddeclare

Then you write their name in the prose, because a name is a word:

Mr. Petrakis looks you up and down without saying anything for a long time.

Declaring a person is the same idea as declaring a variable, for the same reason. Nothing about the word “Petrakis” sitting in a paragraph tells the next writer that he exists, so two fragments invent two different Petrakises under one name, and a third introduces the player to a stranger they have known for years. *declare says nobody has written him yet and carries the description a maintainer records on acceptance; *use names somebody who already exists.

Add reserved: true to ask to be consulted before other writers build on someone. The world is open by default. See World Entities.