Open app
Moonborn — Developers

Interactive fiction prototyping

Stand up a playable prototype with three NPCs in an afternoon. Wire Moonborn into a minimal CLI or web shell; iterate on writing before committing to an engine.

You don't need a game engine to test whether a cast holds together. A CLI + the chat API is enough for the writing iteration.

The minimum

import Moonborn from '@moonborn/sdk';
import readline from 'node:readline';
 
const client = new Moonborn({ apiKey: process.env.MOONBORN_API_KEY });
 
// 1. Pull or create three personas + relationships.
const [merchant, traveler, tavern] = await loadCast();
 
// 2. Open one shared session.
const session = await client.chat.createSession({
  personaId: tavern.id,
  ensemble: [merchant.id, traveler.id],
  metadata: { sceneId: 'tavern_prototype' },
});
 
// 3. Loop.
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
function prompt(): void {
  rl.question('You > ', async (text) => {
    if (text.trim() === ':quit') return rl.close();
    // Author picks the speaker by name prefix:
    //   "merchant: We don't serve outsiders." → merchant says this line.
    //   "what just happened?" → narrator-mode, pick whoever fits.
    const [tag, ...rest] = text.split(':');
    const speakerName = rest.length > 0 ? tag.trim() : 'narrator';
    const content = rest.length > 0 ? rest.join(':').trim() : text;
    const speaker = matchSpeaker(speakerName) ?? tavern;
    const reply = await client.chat.sendMessage({
      sessionId: session.id,
      speaker: speaker.id,
      content,
    });
    console.log(`${speaker.surface.name.display} > ${reply.content}`);
    if (reply.driftAlert) console.warn('  (drift)');
    prompt();
  });
}
prompt();

What you get

  • Writing iteration in minutes, not weeks.
  • Drift scores per turn — see which character starts slipping when the scene heats up.
  • Long-term memory persists across runs — your prototype can have callbacks.

What you lose vs a real engine

  • Player UI (just CLI text).
  • Branching scene state (you script linearly).
  • Save / load beyond the session ID.

Graduating to a real engine

When the cast holds together in the CLI, the session IDs port unchanged into your engine of choice — Unity, Godot, Twine, custom. The Moonborn side doesn't care which UI hosts the conversation.

Related