For game developers
Story logic you can drop into any engine
Writers build the story in the editor. The engine plays it inside your game and hands your code what happened — and how any of it looks, sounds or saves is yours.
TypeScript
C
C++
C#
Dart
WebAssembly
A choice doesn’t have to be a button
A choice is an index your game passes, so it can come from anything the game knows about — not only from text the player clicks. And every choice can carry a condition, so what is on offer follows the world: where the player stands, how a character feels about them, what a faction remembers.
That works because the story and your game share their state, all the time, in both directions.
- Somewhere the player goes: the game writes where they are, and a choice opens
- Someone the player knows: a relationship that grows over the story opens what was closed
- Something the player does: the game takes a choice itself, from a door, an item or a gesture
- The clock: a passage takes its own choice when its countdown runs out
Your game and the story talk to each other
Console variables and entities are the shared memory: the story moves them as it plays, your game moves them as the player does, and each side hears about the other’s changes. Callbacks name the moments that matter, so gameplay can react to the story — and the story to gameplay.
- Into the story: choices and answers, console variables written and reset, entities removed, the language changed, a save resumed.
- Out of it: every passage and question as data, what moved after each step — variables, entities and what the story remembers — and the full cast of entities with their typed attributes whenever you ask.
- Named callbacks: moments a writer declares in the editor on twenty kinds of event, aimed at a board, a passage, a variable, a component or a state.
- A callback carries no code and cannot change the story; your game decides what it means
session.writeCvar('zone', 'jetty')
The player walked onto the jetty.
{ kind: 'changed', cvars: [zone] }
The story saw it, and says what moved.
callback reachedTheJetty
A named moment the writer declared — cvar.changed, aimed at zone.
session.choose(1)
The game takes “Take the boat” — no button pressed.
{ kind: 'show', passage }
The next passage, for the game to draw its own way.
callback brannocTrusts
entity.field.changed, aimed at Brannoc’s trust.
session.entities
Read the cast: Brannoc { trust: 3 }, every attribute typed.
Four bindings, one engine
Each binding is its own open repository and its own package, built against a published engine release and pinned to it. The bindings are MIT; the engine is free and royalty-free to ship, unmodified, inside your product.
- TypeScript and JavaScript on npm: the engine as a WebAssembly component, and an optional Web Audio speaker for its soundscapes.
- C and C++ through CMake: the engine’s C ABI, fetched as a released binary, with a header-only C++17 layer over it.
- C# on NuGet:
netstandard2.1, with the engine with or without Python in its own package. - Dart on pub.dev, for Flutter and Flame — natively, or in a browser over the same web component.
npm install @ripplefx/player
import { Campaign, Player } from '@ripplefx/player';
const campaign = await Campaign.load('/campaigns/the-keeper/');
const player = new Player(campaign.session({ seed: Date.now() }), {
onEffect(effect) {
if (effect.kind === 'show') drawPassage(effect.passage);
},
});
player.start();
Fits a custom engine, a tool, or a page
Nothing in the engine assumes a game engine of its own, a main loop, an audio device or a renderer. It is a library that answers calls, so it fits an in-house engine, an editor tool, a server or a web page as readily as a commercial one.
- A C ABI, so anything with a foreign-function interface can host it
- Desktop, Android and iOS builds, and a WebAssembly component for the browser
- Your clock: a hold is milliseconds you wait, never a timer the engine runs
- Your speaker: a soundscape is a schedule your audio code plays, or ignores
- A no-Python engine build for hosts that want none, and an export checked against it
Your own engine
over the C ABI
An in-house tool
C#, Dart or C++
A web page
a WebAssembly component
A phone
Android and iOS builds
Open a folder and start writing
No account, no project scaffold, nothing written into your directory that you did not ask for.