Eine BIF-Geschichte besteht aus Markdown-Dateien mit ein paar eigenen Schreibweisen für Entscheidungen, Variablen und Bedingungen. Hier stehen sie zum Nachschlagen, wenn du deine eigene Geschichte schreibst. Wie alles zusammenspielt, zeigt die Aktivität »Interaktive Geschichten schreiben«.
Eine Geschichte besteht aus Seiten. Jede .md-Datei im Geschichtenordner ist eine Seite, und ihr Name ohne .md ist ihre Seitennummer: 2.md ist Seite 2. Statt Zahlen gehen auch Wörter, keller.md ist dann die Seite keller; Groß- und Kleinschreibung zählt.
Jede Geschichte beginnt bei 1.md. Nur dort steht ganz oben der Titel der Geschichte, mit einem #:
# Nach Schulschluss
Du willst gerade gehen, da fällt dir ein: Deine Projektmappe liegt noch im Materialschrank.
Für Zwischenüberschriften auf einer Seite nimmst du ##.
Eine Entscheidung ist ein Listenpunkt mit einem Link: In eckigen Klammern steht, was die Leserin oder der Leser sieht, in runden Klammern die Seite, auf der es weitergeht.
- [Sieh im Büro nach.](3) - [Gehe ins Treppenhaus.](4)
Stehen mehrere Entscheidungen untereinander, verzweigt sich die Geschichte. Wege dürfen später wieder zusammenlaufen: Mehrere Seiten können zur selben Seite führen.
(3), nicht (3.md). Mit .md ist es für BIF ein gewöhnlicher Link und keine Entscheidung.
Mit einem Punkt statt einer Seitennummer bleibt die Geschichte auf derselben Seite. Was nach der Auswahl erscheint, steht direkt darunter, mit vier Leerzeichen eingerückt:
- [Frage nach dem Materialschrank.](.) > "Der ist abgeschlossen. Der kleine Schlüssel hängt hier neben der Tür." - [Gehe zurück in den Flur.](2)
Eine lokale Entscheidung lässt sich bei einem Besuch der Seite einmal auswählen. Wer die Seite verlässt und wiederkommt, kann sie erneut wählen; was dauerhaft gelten soll, merkst du dir deshalb in einer Variable.
In einem <script>-Block gibst du einer Variable einen Wert. Sie behält ihn für den Rest der Geschichte, auch wenn die Leserin oder der Leser die Seite wechselt.
<script> has_key = false; points = 0; name = "Lea"; </script>
Ein Skript läuft bei jedem Besuch seiner Seite. Anfangswerte gehören deshalb nach 1.md, die nur am Anfang besucht wird. Steht ein Skript eingerückt unter einer lokalen Entscheidung, läuft es erst, wenn diese Entscheidung gewählt wird:
- [Nimm den Schlüssel.](.) <script> has_key = true; </script> Du nimmst den kleinen Schlüssel vom Haken.
In der Entwicklungsansicht zeigt der Bereich State, welchen Wert jede Variable gerade hat.
Eine Bedingung entscheidet, ob eine Entscheidung oder ein Text gerade erscheint. Hinter einer Entscheidung steht sie in geschweiften Klammern, um einen Text herum in einem <p>:
- [Öffne den Materialschrank.](5){condition="has_key"} <p condition="!has_key"> Neben der Tür hängt ein kleiner Schlüssel an einem Haken. </p>
Bedingungen sind JavaScript-Ausdrücke:
| Bedingung | stimmt, wenn … |
|---|---|
has_key |
has_key den Wert true hat |
!has_key |
has_key nicht true ist |
points >= 3 |
points mindestens 3 ist (auch >, <, <=) |
name == "Lea" |
name genau Lea ist (!=: ungleich) |
has_key && points > 2 |
beides stimmt |
has_key || has_map |
mindestens eins von beiden stimmt |
Eine Seite, auf der keine Entscheidung zu einer anderen Seite führt, ist ein Ende. BIF zeigt dort den Knopf »Gedrückt halten, um erneut zu spielen«; nach einem Neustart beginnt die Geschichte wieder bei 1.md, und die Anfangswerte dort gelten von Neuem.
Eine Geschichte kann mehrere Enden haben: ein gutes, ein schlechtes, ein überraschendes.
Welche Geschichte BIF zeigt, steht in config.js. Für eine eigene Geschichte legst du neben pages-starter einen neuen Ordner an, zum Beispiel pages-meine-geschichte, darin eine 1.md, und trägst ihn ein:
export const path = "pages-meine-geschichte";
Der Name muss genau so geschrieben sein wie im Explorer, in Anführungszeichen. Bilder, Töne und Videos der Geschichte gehören ebenfalls in diesen Ordner.

BIF liest deine Geschichte bei jedem Speichern und meldet unten im Bereich Problems, in welcher Datei und Zeile etwas nicht stimmt; im Graphen ist die Seite rot markiert. Die Meldungen sind auf Englisch:
| Meldung beginnt mit | Bedeutung | Was hilft |
|---|---|---|
Choice … links to missing page |
Eine Entscheidung führt zu einer Seite, die es nicht gibt. | Die Datei anlegen oder die Nummer in ( ) korrigieren. |
Page … is unreachable |
Keine Entscheidung führt zu dieser Seite. | Eine Entscheidung zu ihr schreiben, oder die Seite löschen. |
Unexpected token |
Tippfehler in einem <script>. |
Die Zeile ansehen: fehlt etwas nach =, eine Klammer, ein Anführungszeichen? |
Invalid condition |
Eine Bedingung ist kein gültiger Ausdruck. | Mit der Tabelle unter »Bedingungen« vergleichen, etwa == statt =. |
Story choices cannot be nested |
Unter einer lokalen Entscheidung steht eingerückt eine weitere Entscheidung. | Die zweite Entscheidung auf die oberste Ebene schreiben, mit einer Bedingung. |
A local choice has no result |
Unter einer lokalen Entscheidung steht nichts Eingerücktes. | Den Text darunter mit vier Leerzeichen einrücken. |
Story asset not found |
Ein Bild, Ton oder Video fehlt. | Liegt die Datei im Geschichtenordner, mit genau diesem Namen, auch in Groß- und Kleinschreibung? |
Image has no alternative text |
Einem Bild fehlt die Beschreibung (nur eine Warnung). | In die eckigen Klammern schreiben, was auf dem Bild zu sehen ist. |
Configured story directory does not exist |
Den Ordner aus config.js gibt es nicht. |
Den Namen in config.js mit dem im Explorer vergleichen. |
Configured story has no 1.md |
Im Geschichtenordner fehlt die erste Seite. | Eine 1.md anlegen. |
Läuft dein Workspace nicht, liest BIF auch nichts. Öffne ihn und speichere eine Seite, dann ist die Liste wieder aktuell.
Das Aussehen deiner Geschichte stellst du ganz oben in 1.md ein. Dafür kannst du einen sogenannten Front-Matter-Block verwenden. Er steht noch vor dem Titel der Geschichte.
Am einfachsten wählst du ein fertiges Theme:
--- theme: mystery --- # Nach Schulschluss
BIF bringt mehrere Themes mit: default, paper, playful, mystery, midnight und terminal.
Ein Theme legt Farben und Schriften fest und verändert außerdem einige Details wie Abstände und abgerundete Ecken. Du kannst es deshalb einfach so verwenden, wie es ist.
Du kannst ein Theme aber auch verändern. Zum Beispiel:
--- theme: midnight accent: "#ff7a18" font_heading: Bungee ---
accent ist die Akzentfarbe für Links und interaktive Elemente. Mit font_heading änderst du nur die Schrift der Überschriften.
Du musst überhaupt kein Theme auswählen. Wenn du theme: weglässt, kannst du die Standarddarstellung selbst mit Farben und Schriften anpassen:
--- background: "#fff7fb" text: "#382d38" accent: "#9d3b77" font_body: Nunito font_heading: Fredoka ---
Die drei Grundfarben haben unterschiedliche Aufgaben:
background – Hintergrundtext – normaler Text und Überschriftenaccent – Links, Hervorhebungen und interaktive ElementeDie übrigen Farbtöne für Flächen, Rahmen und Schatten leitet BIF automatisch daraus ab. Du musst also nicht für jedes kleine Element eine eigene Farbe festlegen.
Mit
brightness: light
oder
brightness: dark
kannst du eine helle oder dunkle Darstellung erzwingen. brightness: system folgt stattdessen der Einstellung des Geräts.
Für font_body und font_heading kannst du den Namen einer Schrift von Google Fonts eintragen. Schreibe den Namen genau so, wie er dort steht. BIF lädt die benötigten Schriftdateien beim Entwickeln automatisch herunter und speichert sie lokal im Ordner bif-assets. Diesen Ordner solltest du deshalb nicht selbst für Bilder oder andere Dateien verwenden.
Für die normalen Texte kannst du die üblichen Markdown-Schreibweisen verwenden.
Fetter Text:
Auf dem Tisch liegt ein **kleiner Schlüssel**.
Kursiver Text:
Aus dem Nebenraum hörst du ein *leises Klopfen*.
Eine Zwischenüberschrift innerhalb einer Seite:
## Ein Hinweis
Denke daran: # verwenden wir für den Titel der gesamten Geschichte. Für Unterüberschriften innerhalb einer Seite beginnt die Hierarchie bei ##.
Bilder speicherst du am besten innerhalb deines Geschichtenordners, zum Beispiel:
pages-meine-geschichte/
├── 1.md
├── 2.md
└── images/
└── key.jpg
In Markdown kannst du das Bild so einfügen:

Der Text in den eckigen Klammern beschreibt das Bild und hilft zum Beispiel Menschen, die einen Screenreader verwenden.

Auch Audio- und Videodateien können Teil einer Geschichte sein:
<audio controls src="audio/door.mp3"></audio>
oder:
<video controls> <source src="video/train.webm" type="video/webm"> </video>
Variablen können nicht nur true und false, sondern auch Zahlen oder Texte enthalten.
Zum Beispiel:
<script> points = 3; </script>
Mit doppelten eckigen Klammern kannst du einen Wert in den Text einsetzen:
Du hast [[ points ]] Punkte.
Wenn eine Variable auf einer Seite einen Anfangswert bekommen soll, die mehrfach besucht werden kann, ist ??= praktisch:
points ??= 0;
Der Wert wird damit nur gesetzt, wenn points noch nicht existiert. Werte wie 0, false oder ein bereits vorhandener Text bleiben unverändert.
Für manche Geschichten kann Zufall interessant sein.
Math.w6()
würfelt mit einem sechsseitigen Würfel.
Math.chance(50)
liefert mit einer Wahrscheinlichkeit von 50 Prozent true.
Zum Ausprobieren kannst du zum Beispiel diesen Block auf eine Seite schreiben:
<script> if (Math.chance(50)) { print("Aus dem Treppenhaus hörst du Schritte."); } else { print("Im Treppenhaus bleibt alles still."); } </script>
Lade die Seite mehrmals über einen Neustart der Geschichte. Mal hörst du die Schritte, mal bleibt es still.
Zufall eignet sich gut für kleine Überraschungen. Wichtige Folgen sind oft interessanter, wenn sie von vorherigen Entscheidungen abhängen.
Bei größeren Geschichten kannst du Seiten im Graphen zu Bereichen zusammenfassen.
Ganz oben in einer Seite kann zum Beispiel stehen:
<!-- Schule -- Büro -->
Eine andere Seite könnte beginnen mit:
<!-- Schule -- Flur -->
Diese Kommentare sind für die Leserinnen und Leser unsichtbar. Sie helfen nur dabei, den Graphen übersichtlich zu halten.

Für normale Entscheidungen reichen die Markdown-Links fast immer aus:
- [Gehe nach links.](5) - [Gehe nach rechts.](6)
JavaScript wird interessant, wenn Entscheidungen nicht einfach fest im Text stehen sollen, sondern erst aus Daten oder dem aktuellen Zustand der Geschichte entstehen.
BIF stellt Skripten dafür unter anderem die Funktionen print, presentChoice und goToPage zur Verfügung. Das folgende Beispiel baut die Entscheidungsmöglichkeiten mit JavaScript zusammen. Wenn has_key den Wert true hat, kommt eine zusätzliche Möglichkeit dazu:
<script> const choices = [ ["3", "Sieh im Büro nach."], ["4", "Gehe ins Treppenhaus."] ]; if (has_key) { print("Du spürst den kleinen Schlüssel in deiner Tasche."); choices.push(["5", "Öffne den Materialschrank."]); } const target = await presentChoice(choices); await goToPage(target); </script>
presentChoice zeigt die erzeugten Entscheidungen an und liefert zurück, welcher Wert ausgewählt wurde. In diesem Beispiel ist das direkt die Nummer der Zielseite. goToPage öffnet anschließend diese Seite.
Mit JavaScript könntest du auf diese Weise zum Beispiel Entscheidungen aus einer Liste erzeugen, kleine Rätsel programmieren oder Abläufe bauen, die mit normalen Markdown-Entscheidungen umständlich würden. Für die meisten Stellen einer Geschichte bleiben Markdown, Variablen und Bedingungen aber übersichtlicher.