Your First Fragment
We’ll build a complete fragment step by step.
What is a fragment?
Section titled “What is a fragment?”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.
1. The header
Section titled “1. The header”Every fragment begins with === and a unique snake_case id, followed by header fields:
=== youth_first_jobphase: youthtitle: The First Summer Jobtags: career, independence, moneyrequires: WEA <= 60phase: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.
Who your fragment shows up for
Section titled “Who your fragment shows up for”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 >= 15Or, with an alternative in it:
requires: (WEA <= 60 || REP <= 40) && !friend_kevinThat 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.
2. The setup prose
Section titled “2. The setup prose”Whatever follows the header is the scene the player reads:
The summer before your last year of school. The new phone isn't goingto buy itself. So on a Tuesday morning you find yourself outside theice 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}.3. The choices
Section titled “3. The choices”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+10on 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.
4. Delayed consequences
Section titled “4. Delayed consequences”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.
5. Branching an outcome
Section titled “5. Branching an outcome”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 *endifAdd 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 *endrandomWeights 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_shiftThe apron doesn't fit. The register doesn't follow your commands.> Focus on the customers *effects: CHA+2> Focus on the machine *effects: INT+2Subscenes 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.
7. Check your work
Section titled “7. Check your work”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.
Advanced
Section titled “Advanced”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.
Variables
Section titled “Variables”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_jobphase: youthtitle: The First Summer Jobtags: career, independence, money
*use lies_told*enduse
*declare local flag told_him_straight local count shifts_worked*enddeclare*declareintroduces a variable. Mark itlocaland it belongs to your fragment alone, stored behind the scenes asyouth_first_job.shifts_worked, so your counter and somebody else’s cannot collide and you can name it whatever reads best.*usenames shared state that already exists, likelies_told. Every shared name is recorded once, with a sentence settling what it means:lies_toldcounts 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
flagis true or false. Acountis a whole number, and the only thing+=and>=accept. Anenumis 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.
Inherent traits
Section titled “Inherent traits”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)*enddeclarerandom(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.*endifThe 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.
People and places
Section titled “People and places”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" }*enddeclareThen 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.