Skip to content

Instantly share code, notes, and snippets.

@mumblequatch
Created March 3, 2026 21:16
Show Gist options
  • Select an option

  • Save mumblequatch/81593704845e5f226a65598eb6c10df0 to your computer and use it in GitHub Desktop.

Select an option

Save mumblequatch/81593704845e5f226a65598eb6c10df0 to your computer and use it in GitHub Desktop.
CYOA Project — SugarCube Inky-style engine, publish script, template

CYOA Project — Handoff Notes

What This Is

An interactive fiction engine that replicates Inky's preview player using SugarCube Twee files. Stories render as a continuous scroll of accumulating text with fade-in paragraphs, dividers between passages, and styled choices — matching Inky's aesthetic and behavior.

The first story built with it is The Cliff Wraith, live at: https://mumblequatch.github.io/the-cliff-wraith/

Project Structure

260303_ CYOA Project/
├── Script/
│   └── 15_The_Cliff_Wraith.twee   ← latest clean version
├── inky-player-template.twee       ← start new stories from this
├── publish-twee.sh                 ← reference copy of the publish script
└── handoff-notes.md                ← this file

Installed on Mac:

  • ~/scripts/publish-twee — the publish script (invoked via Keyboard Maestro)
  • ~/.tweego/ — tweego compiler + SugarCube 2.37.3 (auto-installed on first publish)

GitHub: mumblequatch/the-cliff-wraith — GitHub Pages enabled, deploys from main.

How the Engine Works

The StoryScript and StoryStylesheet passages contain a custom rendering engine that intercepts SugarCube's normal output. Key mechanics:

  1. SugarCube's #passages div is hidden (via visibility: hidden, NOT display: none — the latter breaks the :passagedisplay event).
  2. A custom #scroll-container > #text-buffer structure is injected into the body.
  3. On each :passagedisplay event, the engine grabs the passage's raw HTML, splits it on double <br> breaks (SugarCube does NOT wrap text in <p> tags), wraps each block in a <p>, and appends it to #text-buffer.
  4. Link detection: if a <p> contains a data-passage link AND surrounding text, it's an inline link (stays in prose, always blue). If a <p> contains ONLY a link, it's a standalone choice (grey on desktop hover, blue on mobile/tap).
  5. Fade-in uses jQuery .animate() with 200ms minimum separation between elements.
  6. Smart scroll: short passages scroll to show content at the bottom; tall passages scroll to the top of the new content so the reader starts at the beginning.
  7. Session reset: sessionStorage.clear() on load so browser refresh restarts the story.

SugarCube Macros

<<set>>, <<if>>/<<else>>/<<elseif>>, and <<link>> all work. SugarCube processes them before :passagedisplay fires, so the engine only ever sees the resulting HTML. Example from The Cliff Wraith:

:: take_card
<<set $hasCard to true>>You slip the card into your jacket pocket.

:: meeting_amanda
<<if $hasCard>>You pat your pocket — Kendrick's card is still there.<<else>>You wish you'd taken that cabbie's card. Too late now.<</if>>

Starting a New Story

  1. Copy inky-player-template.twee and rename it.
  2. Replace My New Story in :: StoryTitle.
  3. Generate a new IFID (any UUID generator, or uuidgen in Terminal).
  4. Set "start" in :: StoryData to your first passage name.
  5. Write passages. The .twee extension is just a text file — you can rename to .txt to edit, then rename back.

Publishing

The KM shortcut triggers ~/scripts/publish-twee, which:

  1. Opens a file picker for the .twee file
  2. Compiles to HTML via tweego
  3. Prompts for a GitHub repo name (defaults to the StoryTitle, slugified)
  4. Creates the repo if it doesn't exist, or updates it if it does
  5. Enables GitHub Pages and opens the URL in the browser

First run auto-installs tweego (x64 binary, works on Apple Silicon via Rosetta) and SugarCube 2.37.3 to ~/.tweego/.

Visual Specs

  • Font: Atkinson Hyperlegible Next, weight 300, 19px, line-height 1.75
  • Colors: #CCCCCC text on #1A1A1A background
  • Choices: #555 grey on desktop (blue #06C2FE on hover), blue by default on mobile
  • Inline links: always #06C2FE, underline on hover
  • Dividers: 1px #333 between passages
  • Max width: 580px centered column

Known Gotchas

  • SugarCube does NOT wrap passage text in <p> elements. It renders raw text nodes + <br> + <a>. The engine handles this by splitting on <br><br> and wrapping blocks manually.
  • #passages must use visibility: hidden (not display: none) or SugarCube won't fire its :passagedisplay event.
  • The tweego macOS build is x64 only — no arm64 release exists. Works fine on Apple Silicon through Rosetta.
  • The ! characters in the Dropbox path (!Inbox/!PRO/) require single quotes in zsh to avoid history expansion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment