<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Taufik Nur Rahmanda</title>
    <description>The latest articles on DEV Community by Taufik Nur Rahmanda (@txufiknr).</description>
    <link>https://dev.to/txufiknr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4057438%2Feb154fee-5943-4760-b26d-0b839a22b55d.png</url>
      <title>DEV Community: Taufik Nur Rahmanda</title>
      <link>https://dev.to/txufiknr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/txufiknr"/>
    <language>en</language>
    <item>
      <title>How I Built an AI-Powered Interactive Fiction Platform with 9 LLM Providers, RAG, and Next.js</title>
      <dc:creator>Taufik Nur Rahmanda</dc:creator>
      <pubDate>Sat, 01 Aug 2026 11:19:47 +0000</pubDate>
      <link>https://dev.to/txufiknr/how-i-built-an-ai-powered-interactive-fiction-platform-with-9-llm-providers-rag-and-nextjs-5e50</link>
      <guid>https://dev.to/txufiknr/how-i-built-an-ai-powered-interactive-fiction-platform-with-9-llm-providers-rag-and-nextjs-5e50</guid>
      <description>&lt;p&gt;When I was a kid, I devoured those short, fast-paced branching horror books — the Goosebumps-era choose-your-own-nightmare stories where the ending depended on which page you flipped to. Years later, after nearly a decade as a full-stack engineer, I started wondering something.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Could AI generate an interactive novel that actually remembers everything?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not a choose-your-own-adventure with pre-written branches. A living story — one that tracks what you did fifty pages ago, holds a grudge, and waits for the right moment to twist the knife.&lt;/p&gt;

&lt;p&gt;That question became &lt;strong&gt;Twistloom&lt;/strong&gt;. And what started as a curiosity project turned into a distributed AI orchestration system spanning 9 LLM providers, RAG-based semantic memory, and a structured story engine that refuses to forget.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq2iunmgg5k81mouu4p5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq2iunmgg5k81mouu4p5d.png" alt="Twistloom homepage screenshot" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  People imagine AI storytelling as a single prompt
&lt;/h2&gt;

&lt;p&gt;Usually, people picture AI fiction like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write me a horror story...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;But after building Twistloom, I realized the hardest part was never generating text. LLMs are astonishingly good at that. The hard part is &lt;strong&gt;maintaining consistency across hundreds of pages while every reader continuously changes the story&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A character shouldn't suddenly change personality. A missing key shouldn't magically reappear. Someone who died fifty pages ago shouldn't casually walk back into the scene. Every generated page has to understand everything that happened before it — and that problem turned out to be far more interesting than generation itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Twistloom actually is
&lt;/h2&gt;

&lt;p&gt;For a reader, Twistloom looks deceptively simple.&lt;/p&gt;

&lt;p&gt;You type a story idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A detective in a city where the rain never stops starts receiving letters from victims who haven't died yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Within minutes, a full branching psychological thriller appears. Every page ends with choices — generated by the AI, tailored to your character's situation. Pick one, and the world responds.&lt;/p&gt;

&lt;p&gt;And sometimes, the AI doesn't offer you the option you want at all. So you can &lt;strong&gt;type your own&lt;/strong&gt; — a custom action, anything. The system checks if it's plausible, scores it, and generates a page for it anyway. You tried to pick the locked door? It'll write what happens when you do.&lt;/p&gt;

&lt;p&gt;There are three story shapes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it feels like&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Novel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A straight line — one page after another, toward a single ending.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interactive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A choose-your-own-nightmare — every choice closes one door and opens another.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiverse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every choice generates &lt;em&gt;multiple alternate realities&lt;/em&gt;. Re-read the same book and uncover endings you didn't know existed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last one is the one people don't believe at first. In Multiverse Mode, &lt;strong&gt;two readers making identical choices may still walk away with different stories&lt;/strong&gt;. The system continuously generates candidate branches in the background, so spoilers are effectively dead. No two playthroughs are the same.&lt;/p&gt;

&lt;p&gt;The choices echo through the story: characters remember kindness and betrayal, your sanity meter rises and falls with the tension, clues you missed stay buried — and the ending adapts to what you actually did, not what an author planned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The engineering challenge wasn't AI — it was memory
&lt;/h2&gt;

&lt;p&gt;Here's the thing about writing with an LLM: a text-generation call has no memory of anything except what you stuff into its context window. If you want a coherent 300-page branching novel, you can't just paste the whole book in every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq7msdvdc4p0agptaukgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq7msdvdc4p0agptaukgt.png" alt="Scene information of current story page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So Twistloom doesn't treat the story as flat text at all. &lt;strong&gt;Every page carries a structured narrative state snapshot.&lt;/strong&gt; Behind the scenes, the backend tracks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;character psychology, archetype, and stability&lt;/li&gt;
&lt;li&gt;relationships and interaction history&lt;/li&gt;
&lt;li&gt;discovered clues and open mysteries&lt;/li&gt;
&lt;li&gt;places, locations, and their atmosphere&lt;/li&gt;
&lt;li&gt;facts history — durable fact evolution per entity&lt;/li&gt;
&lt;li&gt;the world clock, mood, weather, date and time&lt;/li&gt;
&lt;li&gt;scene momentum and narrative threads&lt;/li&gt;
&lt;li&gt;hidden state — truth level, threat proximity, reality stability, and the planned ending&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the AI writes the next page, it doesn't just get the previous paragraph. It gets the &lt;em&gt;state of the world&lt;/em&gt;. Consistency stops being a hope and becomes a data structure.&lt;/p&gt;

&lt;p&gt;And there's a lot of state. To keep reconstruction fast, I built a &lt;strong&gt;hybrid delta + checkpoint architecture&lt;/strong&gt;: snapshots are persisted every 5 pages, and everything between is stored as incremental deltas. Reconstructing a story state is roughly &lt;strong&gt;90% faster&lt;/strong&gt; than replaying every page from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  When context windows aren't enough: RAG with Jina AI + pgvector
&lt;/h2&gt;

&lt;p&gt;Even with structured state, there's a ceiling. Character backstories, world lore, clues discovered thirty chapters ago — you can't ship all of it to the model every single time.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Twistloom embeds narrative memories using &lt;strong&gt;Jina AI embeddings&lt;/strong&gt; and stores them directly in PostgreSQL using &lt;strong&gt;pgvector&lt;/strong&gt;. Instead of replaying the entire novel to the model, only the memories most relevant to the current scene are retrieved at generation time.&lt;/p&gt;

&lt;p&gt;I split semantic memory into &lt;strong&gt;five dedicated embedding tables&lt;/strong&gt; (1024-dimensional, HNSW-indexed for fast ANN search):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;page_embeddings        — the narrative prose itself&lt;/span&gt;
&lt;span class="s"&gt;character_embeddings   — who everyone is, and how they feel&lt;/span&gt;
&lt;span class="s"&gt;place_embeddings       — where scenes happen&lt;/span&gt;
&lt;span class="s"&gt;future_note_embeddings — deferred payoffs scheduled by the world clock&lt;/span&gt;
&lt;span class="s"&gt;clue_embeddings        — the trail of breadcrumbs readers might miss&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a page is about to be written, the engine embeds the current context, queries the nearest memories across these tables, and injects only the relevant fragments into the prompt. Embeddings are inserted fire-and-forget after each page persists — never during delta-chain replay, so semantic memory never slows down state reconstruction.&lt;/p&gt;

&lt;p&gt;The result is a story engine that remembers far more than any context window would allow, without blowing up prompt size.&lt;/p&gt;




&lt;h2&gt;
  
  
  One AI provider wasn't enough
&lt;/h2&gt;

&lt;p&gt;Most AI applications depend on a single provider. That works — until it doesn't.&lt;/p&gt;

&lt;p&gt;Rate limits. Regional outages. Model deprecations. Temporary failures. For an experience where a reader is mid-scene and waiting for the next page, a provider outage isn't an error log — it's a ruined reading session.&lt;/p&gt;

&lt;p&gt;So Twistloom abstracts every provider behind a &lt;strong&gt;common interface&lt;/strong&gt;, and orchestrates &lt;strong&gt;nine different LLM providers&lt;/strong&gt; in a waterfall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini → github → cohere → mistral → groq → cerebras → nvidia → openrouter → cloudflare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one provider is rate-limited, degraded, or down, the request automatically falls through to the next. The business logic never cares &lt;em&gt;which&lt;/em&gt; provider generated a page — providers are interchangeable implementations of the same contract. The reader experience continues regardless of what happens to any single vendor.&lt;/p&gt;

&lt;p&gt;One insight that shaped the whole design: &lt;strong&gt;resilience isn't a feature you bolt on at the end.&lt;/strong&gt; It's an architectural decision you make on day one, in how you define the provider interface. If your code is written against a single SDK, adding a second provider later is a rewrite. Written against a contract, it's a config change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Every page is generated twice
&lt;/h2&gt;

&lt;p&gt;Another thing I learned fast: LLMs occasionally produce surprisingly good stories — and occasionally complete nonsense.&lt;/p&gt;

&lt;p&gt;Instead of trusting a single response, every generation passes through a &lt;strong&gt;two-call pipeline&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;primary call&lt;/strong&gt; generates the page, its actions, and the state updates.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;second call&lt;/strong&gt; — a different model, a different prompt — acts as an evaluator judge. It scores the page for quality, coherence, and correctness before it ever reaches the reader.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbwi5j9lqv51d5zkfvqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwbwi5j9lqv51d5zkfvqe.png" alt="Two different AI models work to produce every single quality story page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of that, every page runs &lt;strong&gt;canon validation&lt;/strong&gt; (&lt;code&gt;runCanonValidationPass()&lt;/code&gt;): the engine checks for timeline contradictions, character knowledge violations, established fact conflicts, and personality inconsistencies. If a page breaks canon, it's flagged and rewritten — at generation time, not discovered by an angry reader three chapters later.&lt;/p&gt;

&lt;p&gt;Custom actions get their own three-gate pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gate 0 (client):&lt;/strong&gt; regex safety patterns guard against prompt injection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate 1 (AI):&lt;/strong&gt; plausibility + progression scoring — will this action move the story forward?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate 2 (generation):&lt;/strong&gt; the outcome page, with full canon validation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the branching contract is enforced at the database layer, not by convention. Novel mode allows one action per page and one destination. Interactive allows 2–6 actions. Multiverse allows 2–6 actions with unlimited destinations. The backend rejects anything that violates the mode contract, so the story graph stays structurally valid no matter what the model outputs.&lt;/p&gt;

&lt;p&gt;Because generation is expensive, books are created &lt;strong&gt;asynchronously&lt;/strong&gt; — a GitHub Actions worker handles the heavy lifting while you watch progress stream in via polling. You can start a book and come back when it's done.&lt;/p&gt;




&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;The whole thing runs on the JAMstack of my dreams:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16&lt;/strong&gt; (App Router, ISR, Server Components) + &lt;strong&gt;React 19&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; across the full stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; with tension-driven theming — the UI colors literally bleed darker as the story's tension rises&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;next-intl&lt;/strong&gt; for full English and Indonesian locale support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Query&lt;/strong&gt; for server state, &lt;strong&gt;Zustand&lt;/strong&gt; for client state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NextAuth v5&lt;/strong&gt; for cookie-based JWT auth, &lt;strong&gt;TipTap&lt;/strong&gt; for the admin's rich editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hono&lt;/strong&gt; — ultra-fast, runtime-agnostic HTTP framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drizzle ORM&lt;/strong&gt; for type-safe SQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neon PostgreSQL&lt;/strong&gt; (serverless) with &lt;strong&gt;pgvector&lt;/strong&gt; for semantic memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; as the async generation runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; for the credit economy, &lt;strong&gt;ImageKit&lt;/strong&gt; for media, &lt;strong&gt;Resend&lt;/strong&gt; for email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;9 LLM providers with automatic fallback&lt;/li&gt;
&lt;li&gt;Jina AI embeddings + pgvector RAG pipeline&lt;/li&gt;
&lt;li&gt;Structured JSON generation, two-call evaluation, canon validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is deployed on Vercel serverless. Public pages are ISR-cached (60–300s), reader pages are cached for fast loads, and the generation UX is built so the modal opens &lt;em&gt;instantly&lt;/em&gt; on click — before the first network request completes — with progress streaming in as the story assembles piece by piece.&lt;/p&gt;




&lt;h2&gt;
  
  
  More than an AI story generator
&lt;/h2&gt;

&lt;p&gt;When I started, I thought I was building a branching horror generator.&lt;/p&gt;

&lt;p&gt;After several months of solo work, I realized I was building something much bigger: an &lt;strong&gt;AI-native interactive fiction platform&lt;/strong&gt;. Not a text generator — a platform where readers get living stories, and where &lt;em&gt;writers&lt;/em&gt; will eventually sit down with an AI as a creative partner, not a replacement.&lt;/p&gt;

&lt;p&gt;My long-term vision isn't AI-authored slop. It's giving real authors a co-pilot that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remembers their lore&lt;/li&gt;
&lt;li&gt;understands their characters&lt;/li&gt;
&lt;li&gt;protects their canon&lt;/li&gt;
&lt;li&gt;and suggests ideas without seizing the pen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI should write &lt;em&gt;with&lt;/em&gt; you — never &lt;em&gt;for&lt;/em&gt; you. That's the difference I care about most.&lt;/p&gt;

&lt;p&gt;Right now the platform is &lt;strong&gt;free to use&lt;/strong&gt;, because I'm running on non-commercial free-tier LLM API keys and can't legally commercialize yet. The credit economy, subscriptions, and gamified systems are all built and ready — the commercial switch is a configuration away.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;This project taught me something unexpected.&lt;/p&gt;

&lt;p&gt;The hardest problem in AI storytelling isn't prompting. It's &lt;strong&gt;software engineering&lt;/strong&gt;. Building resilient orchestration. Managing long-term memory. Designing structured pipelines. Keeping generated worlds internally consistent. Testing, optimizing, and shipping it all as a solo developer.&lt;/p&gt;

&lt;p&gt;Generating text is easy.&lt;/p&gt;

&lt;p&gt;Generating believable worlds is much, much harder.&lt;/p&gt;

&lt;p&gt;If you've made it this far, I'd genuinely love your feedback — whether you're into AI engineering, interactive fiction, or just building unusual things. And if you want to see what an AI-native thriller actually feels like, step into the loom:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://twistloom-web.vercel.app" rel="noopener noreferrer"&gt;https://twistloom-web.vercel.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The story remembers. Every choice leaves a scar.&lt;/em&gt; 🩸&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>interactivefiction</category>
      <category>genai</category>
    </item>
  </channel>
</rss>
