First


# The Interrogator — Development Log

## ๐ŸŽฎ Project Overview

**The Interrogator** (Ukrainian: โ€œะกะปั–ะดั‡ะธะนโ€) is an AI-powered detective game where players interrogate suspects to solve murder cases. Built as a single-page web application with a Cloudflare Worker backend.

- **Live Demo**: https://sashko1391.github.io/interrogator

- **Tech Stack**: Vanilla JS, Cloudflare Workers, Claude Sonnet 4, SDXL Lightning (Replicate)

- **Languages**: Ukrainian / English

-----

## ๐Ÿ“… Development Timeline

### Session 1: Foundation (v1.0 - v2.0)

**Initial concept:**

- 3 pre-built cases (Easy/Medium/Hard difficulty)

- Single suspect per case

- 10-question energy system

- Film noir aesthetic

**Key decisions:**

- Single HTML file architecture (no build tools)

- Cloudflare Worker as API proxy (keeps keys secure)

- Claude Haiku for NPC responses (later upgraded to Sonnet 4)

-----

### Session 2: AI Generation (v3.0)

**Major pivot: Dynamic case generation**

Instead of pre-built cases, the game now generates unique mysteries on-the-fly.

**New features:**

- `/generate` endpoint creates full case with Claude Sonnet 4

- 3 suspects per case (up from 1)

- Random guilt patterns: ONE / TWO / ALL / NONE guilty

- Tabbed UI for switching between suspects

- Per-suspect chat history and suspicion tracking

**Architecture:**

```

User โ†’ Frontend โ†’ Worker โ†’ Claude API โ†’ JSON Case

                        โ†’ Replicate API โ†’ Portrait Images

```

**Challenges solved:**

- JSON parsing from LLM (smart quotes, trailing commas)

- Guilty logic validation (auto-fix mismatches)

- Image generation polling (SDXL Lightning via Replicate)

-----

### Session 3: Hardening (v4.0)

**Security audit revealed critical issues:**

|Problem                            |Solution                                    |

|-----------------------------------|--------------------------------------------|

|`/chat` as open Claude proxy       |Validate game markers in system prompt      |

|NERVOUSNESS: 999 injection         |Clamp to 0-100, only increase               |

|Image flooding (9 generations/user)|45s cooldown + cache by emotional state     |

|JSON parsing failures              |Brace counting, quote normalization         |

|Guilty pattern mismatch            |Auto-correct based on actual isGuilty values|

**Added:**

- Content-Length limit (50KB)

- Message length limit (1000 chars)

- Conversation length limit (50 messages)

- Request validation for all endpoints

-----

### Session 4: Scaling Features (v5.0)

**Cost optimization:**

1. **Case Caching**

- Store generated cases in KV

- New users get random cached case with fresh ID

- ~95% reduction in `/generate` Claude calls

1. **Smart Token Management**

- Questions 1-5: 500 tokens (full responses)

- Questions 6+: 200 tokens + โ€œbe briefโ€ instruction

- ~60% token savings after question 5

**New features:**

1. **Daily Case**

- Same case for all users each day

- `GET /daily?lang=ua`

- Enables community discussion & comparison

1. **Streamer Mode**

- Unlimited questions (โˆž)

- Hidden guilty hints (no spoilers for chat)

- Toggle on start screen

1. **Statistics**

- Track plays and correct verdicts per case

- Show โ€œYouโ€™re in top X%โ€ after verdict

-----

### Session 5: Monetization (v6.0)

**Model: Freemium with soft limits**

|Tier           |Limits                    |Price|

|---------------|--------------------------|-----|

|Free           |3 cases + 50 questions/day|$0   |

|Daily Case     |Unlimited                 |$0   |

|Premium 7 days |No limits                 |$5   |

|Premium 30 days|No limits                 |$10+ |

**Implementation:**

1. **IP-based daily limits** (stored in KV)

1. **Premium tokens** (format: `PRO-XXXXXXXXXXXX`)

1. **Email lookup flow** (no webhooks needed):

   

   ```

   User donates โ†’ Admin adds to KV โ†’ User enters email โ†’ Gets token

   ```

**Why not PayPal webhooks?**

- Donate button has no callbacks

- Would need PayPal Business account

- Manual flow works for indie scale

-----

### Session 6: Polish & UX Fixes

**Visual improvements:**

- 3-column category grid on desktop

- Daily Case card full-width below categories

- Case files open by default (player sees context immediately)

**Removed spoilers:**

- Guilty hint (โ€œะ’ะธะฝะฝั– ะดะฒะพั”โ€) removed from header

- Players must deduce without hints

**Fixed last question bug:**

- Was: Question 15 โ†’ instant โ€œFAILโ€ (never see answer)

- Now: Question 15 โ†’ see NPC response โ†’ โ€œTime to make verdict!โ€ โ†’ player decides

-----

## ๐Ÿ—๏ธ Final Architecture

### Frontend (index.html)

```

โ”œโ”€โ”€ Start Screen

โ”‚   โ”œโ”€โ”€ Rules display

โ”‚   โ”œโ”€โ”€ Category cards (War/History/Hollywood)

โ”‚   โ”œโ”€โ”€ Daily Case card

โ”‚   โ”œโ”€โ”€ Streamer Mode toggle

โ”‚   โ”œโ”€โ”€ Limits display

โ”‚   โ””โ”€โ”€ Premium button

โ”œโ”€โ”€ Loading Screen

โ”œโ”€โ”€ Game Screen

โ”‚   โ”œโ”€โ”€ Case header + ID

โ”‚   โ”œโ”€โ”€ Energy & Suspicion bars

โ”‚   โ”œโ”€โ”€ Case files (collapsible)

โ”‚   โ”œโ”€โ”€ Suspect tabs (3)

โ”‚   โ”œโ”€โ”€ Suspect panel (photo + profile)

โ”‚   โ”œโ”€โ”€ Chat log

โ”‚   โ”œโ”€โ”€ Input + Send

โ”‚   โ””โ”€โ”€ Action buttons (Verdict/Share/Restart/Reveal)

โ””โ”€โ”€ Modals

    โ”œโ”€โ”€ Result (Win/Lose)

    โ”œโ”€โ”€ Share

    โ”œโ”€โ”€ Verdict (checkboxes)

    โ”œโ”€โ”€ Premium

    โ””โ”€โ”€ Limit reached

```

### Backend (worker.js)

```

GET  /daily           โ†’ Daily case (free, unlimited)

GET  /case/{id}       โ†’ Shared case lookup

GET  /limits          โ†’ User's daily limits

GET  /stats/{id}      โ†’ Case statistics

POST /generate        โ†’ Create new case (rate limited)

POST /chat            โ†’ NPC conversation (rate limited)

POST /image           โ†’ Portrait generation

POST /verdict         โ†’ Submit result for stats

POST /validate-premium โ†’ Check token validity

POST /lookup-premium  โ†’ Get token by donor email

```

### KV Storage Structure

```

{caseId}              โ†’ Full case JSON (TTL: 2 weeks)

cache-{cat}-{lang}    โ†’ Array of cached case IDs (TTL: 4 weeks)

daily-{date}-{lang}   โ†’ Daily case (TTL: 2 days)

stats-{caseId}        โ†’ { played, correct, percentCorrect }

limits-{IP}-{date}    โ†’ { casesGenerated, questionsUsed }

premium-{token}       โ†’ { email, amount, created }

donor-{email}         โ†’ token string

```

-----

## ๐Ÿ“Š Cost Analysis

### Per-session costs (estimated):

|Action            |Model/Service                     |Cost      |

|------------------|----------------------------------|----------|

|Generate case     |Claude Sonnet 4 (~3K tokens)      |~$0.02    |

|15 questions      |Claude Sonnet 4 (~5K tokens total)|~$0.03    |

|3 portraits       |Replicate SDXL                    |~$0.01    |

|**Total per game**|                                  |**~$0.06**|

### With caching:

- 95% of `/generate` calls use cache โ†’ $0.001

- Effective cost per game: **~$0.04**

### Break-even:

- $5 donation = 125 games covered

- $10 donation = 250 games covered

-----

## ๐ŸŽฏ Key Learnings

### What worked well:

1. **Single-file architecture** — Easy to deploy, no build complexity

1. **Cloudflare Workers** — Fast, cheap, global edge deployment

1. **Claude for NPCs** — Surprisingly good at staying in character

1. **NERVOUSNESS tag** — Simple but effective game mechanic

1. **Daily Case** — Community feature with zero extra cost

### What was tricky:

1. **LLM JSON output** — Always needs sanitization

1. **Image generation latency** — 10-25 seconds is rough UX

1. **PayPal integration** — Donate buttons have no callbacks

1. **IP-based limits** — Mobile networks share IPs

### What Iโ€™d do differently:

1. Start with caching from day 1

1. Use tool/function calls for structured LLM output

1. Consider pre-generating image variants

1. Add fingerprinting alongside IP for limits

-----

## ๐Ÿš€ Future Ideas

### Near-term:

- [ ] PayPal webhooks for auto-premium

- [ ] Pre-generated daily cases (cron job)

- [ ] Leaderboard for daily case

### Medium-term:

- [ ] Multiplayer mode (same case, compare results)

- [ ] Case difficulty rating based on solve rate

- [ ] Achievement system

### Long-term:

- [ ] White-label for training/HR

- [ ] Mobile app (React Native)

- [ ] Voice input/output

-----

## ๐Ÿ“ File Structure

```

/interrogator

โ”œโ”€โ”€ index.html          # Complete frontend (single file)

โ”œโ”€โ”€ worker.js           # Cloudflare Worker backend

โ”œโ”€โ”€ bg-landscape.jpg    # Background (16:9)

โ”œโ”€โ”€ bg-portrait.jpg     # Background (9:16)

โ”œโ”€โ”€ background.mp3      # Ambient music

โ””โ”€โ”€ p_machine.mp3       # Typewriter sound effect

```

-----

## ๐Ÿ™ Credits

- **AI Models**: Claude Sonnet 4 (Anthropic), SDXL Lightning (Stability AI)

- **Hosting**: GitHub Pages (frontend), Cloudflare Workers (backend)

- **Design**: Film noir aesthetic, Playfair Display + Crimson Pro fonts

-----

*Last updated: January 31, 2026*

*Total development time: ~ 18 hours across multiple sessions*

Files

The_Interrogator_itch_redirect.zip Play in browser
14 hours ago

Comments

Log in with itch.io to leave a comment.

First release is live.

Feedback on difficulty and suspect behavior is especially welcome.