READER BEWARE

Branching gamebooks with state as deep as Twine, the soul of a printed novel, and output that is a real ebook file — epub and mobi, readable on any Kindle, reader app, or browser. No special software, no browser extensions, no proprietary runtime: just yaml, jinja2 and markdown compiled into a book.

pip install it read a built book source on GitHub
Reader Beware cover art

§ Why Reader Beware is awesome

Most interactive fiction tools make apps: Twine files need a player, ChoiceScript needs a runner. Reader Beware compiles your story into ordinary ebooks — every branch is a chapter, every choice a link — so the book opens in whatever you already use, from a Kindle at the beach to a phone offline, and it will outlive every browser on earth.

A real ebook, not an app

EPUB 3 packages (proper mimetype, spine, metadata, cover) and mobi via Calibre. No runtime required on the reader's side.

State that follows you

The choice filter carries world state into every future section. Shirts, credits, dead dorks: the story remembers.

Yaml + jinja + markdown

No new language to learn. Sections are markdown templated with jinja2; conditionals and loops come free from the template engine.

Generate stories, don't hand-write them

A narrative section can generate thousands of rooms: this site's maze book is one yaml file that compiled into 811 chapters.

Pluggable everything

jinja filters, markdown2 extensions, metadata and cover via a config file — register your own filters before the build runs.

An editor you run locally

beware serve gives you a flask web studio: cards per section, link tools, live preview and autosave validation.

An epub built by beware build-epub, opened on an e-reader: the red shirt chapter.
The same yaml, another world state: Jane approves the green shirt. Every choice is a real link inside a real ebook.

§ The syntax, in ninety seconds

A story is a yaml file of named sections. Each body is markdown run through jinja2. Links to other sections are written with two filters: explore (plain jump) and choice (jump, and update the world state on the way).

# narative.yaml — a whole book
---
entrypoint:
  body: |
    This is your opening page.

    [put on red shirt]({{ 'put-on-shirt'|choice(shirt_color='red') }})

    [put on green shirt]({{ 'put-on-shirt'|choice(shirt_color='green') }})

put-on-shirt:
  body: |
    You put on the {{ shirt_color }} shirt.

    [And go outside]({{ 'go-outside'|explore }})

go-outside:
  body: |
    Outside you see Jane and she says "{% if shirt_color == 'green' %}I like your green shirt.{% else %}I dislike that colour on you.{% endif %}"

That's the whole surface. Each section+world pair becomes its own chapter of the ebook — so Jane really did say different things to the two readers.

§ Walk-through: from yaml to a book

  1. Write the story. A folder of yaml files, or one file. The section named entrypoint opens the book.
  2. Build html pages for fast preview: beware build-html --narative-path narative.yaml --output output_folder
  3. Build the ebook: beware build-epub --narative-path narative.yaml --output book --book-config book.yaml writes a real book.epub (plus an unzipped debug folder).
  4. Kindle-ready: beware build-mobi … converts through Calibre's ebook-convert, or falls back to a Kindle-ready epub.
  5. Read anywhere. Transfer the file to any reader app — no player, no runtime.
Built html output (debug pages): the entrypoint of "Red or Green?" after beware build-html.

The same build pipeline powers this website: every book linked below was compiled by CI from a yaml file in site/examples-src/ — click a link, open your reader app, play.

§ Example books built from it

A chapter from "Red or Green?" — the go-outside section with shirt_color=green baked into its filename: Jane approves.
The Little Square, entrypoint
Prince of Belair, a generated section

§ The studio editor

beware serve --root stories/ starts a flask webapp that edits stories as a stack of cards — one per section — with markdown toolbars, an explore/choice link builder (autocomplete over your sections, quoted args done for you), a links inspector, and a sandboxed live preview. Autosave validation flags missing entrypoints or undefined targets while you type; ctrl+S writes canonical yaml.

The story index: every yaml under your root folder, click to open the studio.
The section studio with a live preview open on the entrypoint card — jinja rendered, markdown converted, no build needed.
The explore/choice link tool building a state-carrying link; the finished markdown lands at your cursor.
Power-editing: the raw yaml editor with autosave validation stays one click away.

§ Try it

# python 3.8+; linux tested, contributions welcome elsewhere
pip install -e git+https://github.com/glassresistor/reader-beware.git#egg=reader-beware

# or clone it and run the tests: green across python 3.8 through 3.13
git clone https://github.com/glassresistor/reader-beware.git
cd reader-beware && pip install -e .[test] && pytest

This very page was built by GitHub Actions from the same pipeline: tests, black formatting checks, package installs, book builds and every rendered example on it — see the deploy.yml workflow. Reader beware indeed.