No description
  • JavaScript 87.9%
  • CSS 6.8%
  • C++ 5.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-05-27 14:07:23 +01:00
dist Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
docs Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
examples/static-site Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
scripts Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
src Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
.gitignore Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
.prettierignore Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
LICENSE Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
package-lock.json Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
package.json Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00
README.md Extract Sai Ink embedder 2026-05-27 14:07:23 +01:00

Sai

Ink stories that wake up inside static sites.

Sai is a small browser embedder for compiled Ink stories. It is not an Ink engine; it uses inkjs for that. Sai owns the article-shaped part: loading a story into an existing page, reading a visual manifest, rendering transcript, visual-novel, or cinematic layouts, and staying pleasant to drop into Quartz, Eleventy, Astro, Hugo, plain HTML, or whatever shrine of markup you dragged home at midnight.

The name is the pitch: ink that becomes a living scene.

Why Sai Exists

Most Ink web players answer: "Can I play this story in a browser?"

Sai answers: "Can this story become part of a page?"

It is for interactive essays, lore posts, documentation rituals, cheap visual novels, pitch decks, character sermons, branching articles, and other story objects that want to live inside a site instead of becoming a separate web app.

Quick Start

Copy the built files from dist/ into your site's static assets:

sai.js
sai.css
ink.js

Add the assets to a page:

<link rel="stylesheet" href="/static/sai/sai.css" />
<script defer src="/static/sai/sai.js"></script>

Embed a story:

<div
  class="sai-player"
  data-ink-format="visual-novel"
  data-ink-story="/stories/agora/story.ink.json"
  data-visual-manifest="/stories/agora/visual-manifest.json"
  data-scene-label="The Aquarium Agora"
></div>

Sai auto-initialises every .sai-player when the document loads. It also listens for Quartz-style nav events after client-side page swaps.

Formats

Raven

<div class="sai-player" data-ink-story="/story.ink.json"></div>

The default format is a compact interactive-fiction transcript. It is the simplest Raven Collective-style shape: text, choices, variables when useful, and no stage machinery.

Visual Novel

<div
  class="sai-player"
  data-ink-format="visual-novel"
  data-ink-story="/story.ink.json"
  data-visual-manifest="/visual-manifest.json"
></div>

Visual-novel mode renders one scene image, a fixed dialogue bar, speaker avatar, speaker name, line text, and a right-side option rail. Ink tags drive the speaker and scene:

# speaker: Void
# scene: agora
# avatar: void
The world is full of systems that say one thing and reward another.

Cinematic

<div
  class="sai-player"
  data-ink-format="cinematic"
  data-ink-story="/deck.ink.json"
  data-visual-manifest="/deck.visual-manifest.json"
></div>

Cinematic mode is for slide/deck-like fragments: background plates, captions, references, and short blocks of text.

Data Attributes

  • data-ink-story: required URL for compiled Ink JSON.
  • data-ink-format: raven, visual-novel, or cinematic.
  • data-visual-manifest: optional visual manifest URL.
  • data-scene-label: optional fallback scene label for visual-novel mode.
  • data-ink-title: optional player title/status text.
  • data-ink-runtime: optional URL for ink.js.
  • data-sai-runtime: alias for data-ink-runtime.
  • data-show-variables="true": show Ink variables in visual modes.

Legacy .aetheria-ink-player containers are still initialised so existing GameCult pages can migrate without ritual screaming. New work should use .sai-player.

JavaScript API

window.Sai.init(document.querySelector(".sai-player"));
window.Sai.initAll();
console.log(window.Sai.version);

By default, sai.js loads ink.js from the same directory as itself. Override that with data-ink-runtime, data-sai-runtime, or window.Sai.runtimeSrc.

Visual Manifest

See docs/manifest.md.

Development

npm install
npm run build
npm run check

Compile the example story after dependencies are installed:

npm run compile:example

Open examples/static-site/index.html through a local static server. Browsers are right to dislike fetch() from file://; do not take it personally.

Boundary

Sai owns:

  • browser-side Ink loading through inkjs
  • player DOM and mode rendering
  • visual manifest interpretation
  • default CSS and theme variables
  • static-site auto-init

Sai does not own:

  • authoring Ink
  • compiling .ink to .ink.json
  • Quartz page chrome
  • GameCult-specific assets, lore, or characters
  • a full VN/game engine loop

The brush is sharp. The painting is yours.