Skip to content

Repository files navigation

Exquisite Corpse Machine

I made this because I got tired of every AI writing tool trying to be helpful.

You drop in a sentence. Claude picks up from it. You pick up from Claude's version, but you only see the last few lines, not your own original. Loop it. See what the thing becomes.

It's not a writing assistant. It's a mutation engine.

Named after the surrealist parlor game where you pass a folded piece of paper around the table and each person only sees the last line. The result is a sentence no single person could have written. That's the part I wanted to build for.

Why this exists (and why I'll fight you on "improvements")

Every AI writing tool optimizes for coherence. Your voice. Your intent. Your through-line. I don't want any of that here.

Drift is a feature, not a bug.

The further the tape gets from your seed, the more interesting the session. If you think "oh it'd be so much better if the AI remembered the whole context" or "we should make it stay on topic" — that's a fundamental misread. You just re-invented a chatbot. There are a lot of those. This isn't one.

The fold (each contributor sees only the last N lines) is the whole point. It's enforced inside src/engine/claude.ts and src/engine/gemini.ts so it literally can't leak. Not a convention, a constraint.

Best for:

  • Joke setups where you want to find the punchline by losing control of it
  • Weird short fiction
  • Ideation when you're stuck and coherent tools are making it worse
  • Watching two AIs slowly derail a premise in auto mode (underrated)

Run it

npm install
npm run dev

You bring the keys:

Keys live in your browser's localStorage. Nowhere else. There's an import/export button if you want to move them to another device without retyping.

What's in it

  • Seed + fold depth 1 to 5 (1 = maximum drift, 5 = more coherent. 3 is usually right)
  • Alternating, auto (AI ↔ AI), or custom rotation
  • Five tones: none, dark, absurd, clinical, lyrical. None is honest — it lets the model do what it wants
  • Drift meter (Jaccard similarity, green → amber → red as the tape strays from the seed)
  • Mutation timeline sparkline, click a spike to jump to that segment
  • Exports: plaintext, JSON, styled tape HTML, annotated mutation report HTML
  • Share links (gzip + base64url in the URL fragment, cap 30KB)
  • Auto-retry with exponential backoff, max 3 tries, skips on 4xx so you're not hammering anyone's API

Test it

npm run test          # vitest watch
npm run test:run      # vitest single run (130+ tests)
npm run test:e2e      # playwright smoke (run `npx playwright install chromium` once first)

How it's laid out

src/
├── engine/        pure — fold, rotation, prompts, drift, claude/gemini streamers
├── state/         types + useSession reducer + localStorage
├── ui/            presentational components only
├── lib/           export, share-link codec
└── App.tsx        turn-loop glue

Engine is pure and tested. UI is presentational only, props in and callbacks out. App.tsx is the one place it all wires together. If you find yourself putting logic somewhere else, stop, it belongs in the engine.

Where it lives

npm run build
# dist/ goes wherever you host static stuff

Mine's at tools.snackdriven.com/ecm. No backend. No server state. Nothing to run. If you fork it, it'll just work on Netlify or Cloudflare Pages or a USB stick you forgot about.

A note to future me

If I come back in six months and find a "Continue from seed" button, a summary pass that compresses the full history before each turn, or a setting that lets you "ensure narrative coherence" — delete it. That's the thing I was specifically not making.

About

you and Claude pass a story, each seeing only the last few lines. surrealist mutation engine.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages