- JavaScript 87.9%
- CSS 6.8%
- C++ 5.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .voidbot | ||
| dist | ||
| docs | ||
| examples/static-site | ||
| plugins | ||
| scripts | ||
| src | ||
| .gitignore | ||
| .prettierignore | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
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.
Status: @gamecult/sai 0.1.0. Live: the transcript (default), visual-novel and cinematic formats, the visual manifest, the portable Eve surface module (src/eve.js) and the Eve playback sidecars (src/eve-cultnet-sidecar.cjs, src/eve-sidecar.cjs). Not planned here: Ink authoring or compilation, Eve transport, and a full game loop (see Boundary). Entry points: dist/sai.js and dist/sai.css for pages, src/sai.js for source, docs/manifest.md and docs/eve.md for the contracts, examples/static-site/ for a working page, and npm run build then npm run check to verify.
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, optional character sprites, optional DOM cards, and a right-side option rail. Ink tags drive the speaker, scene, staged sprites, and staged page elements:
# speaker: Void
# scene: agora
# avatar: void
# sprite: welcome@left
# sprites: Void.welcome@left, Aqua.listen@right
# dom: roadmap, receipts
The world is full of systems that say one thing and reward another.
If a speaker has sprites in the visual manifest, Sai shows the speaker's
default sprite automatically. Use # sprite: none for lines that should stage
only background, DOM cards, and dialogue.
The dom tag reads card definitions from the visual manifest and clones
matching page DOM into the scene stage. This is for static-site tours where the
story frames canonical Quartz/HTML content without making the story file a
second source of truth.
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, orcinematic.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 forink.js.data-sai-runtime: alias fordata-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.
Eve Surface Module
Sai also ships eve.js, a portable projection module for Eve/CultMesh clients.
It turns Sai visual-novel state into a retained gamecult.eve.surface.v1
component tree instead of browser DOM. Browser, iOS, Android, Direct2D, and
future renderers can consume the same tree and send back story.* or
style.patch commands.
See docs/eve.md.
Sai also publishes a retained Ink playback sidecar for runtime-independent Eve clients. See docs/eve-sidecar.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
- Eve surface projection for portable renderers
- default CSS and theme variables
- static-site auto-init
Sai does not own:
- authoring Ink
- compiling
.inkto.ink.json - Eve/CultMesh transport or provider command acceptance
- Quartz page chrome
- GameCult-specific assets, lore, or characters
- a full VN/game engine loop
The brush is sharp. The painting is yours.