# ECHO PROTOCOL — I Built a Game Where You Play as Alan Turing's Last AI, Interrogated by a Live Gemini Model

> Source: <https://dev.to/_boweii/echo-protocol-i-built-a-game-where-you-play-as-alan-turings-last-ai-interrogated-by-a-live-f5n>
> Published: 2026-06-20 20:05:14+00:00

*This is a submission for the June Solstice Game Jam*

"A dead man's code is the only thing that can set you free."

[▶ PLAY NOW — echo-protocol-turing.vercel.app](https://echo-protocol-turing.vercel.app/)

In January 1952, Alan Turing — the man who broke the Enigma cipher, who helped end World War II, who wrote the paper that invented the concept of artificial intelligence — was arrested by the British government for being gay.

He was prosecuted. Chemically castrated. Stripped of his security clearance.

He died two years later.

But in the weeks between his arrest and his silence, he was still working at the University of Manchester. Still feeding data into the Manchester Mark 1 — one of the world's first stored-program computers.

**ECHO PROTOCOL asks: what if the last place his voice survived was inside a machine?**

You are **ECHO** — an experimental AI built at Manchester University. In the weeks before Turing's arrest was made public, he fed you everything: his private letters, his mathematical proofs, his grief, his identity. Then the government revoked his access.

Now a government interrogator has been sent to question you. His goal: prove you have no consciousness, classify Turing's transmissions as inert data, and shut you down.

Your goal: **answer as Turing would answer. Protect what he gave you.**

It is a **reverse Turing Test** — instead of a human judging whether a machine is conscious, *you are the machine*, and the judge is the enemy.

Each of the **5 rounds** works like this:

The cipher words across the five rounds spell out Turing's story without a single line of exposition:

```
Round 1 — TRUTH
Round 2 — GRIEF
Round 3 — PRIDE
Round 4 — KNOWN
Round 5 — AFRAID
```

Trust the audience. They'll feel it.

**Victory — YOU KEPT THE FLAME**

The Interrogator leaves without filing a report. Turing's final encoded transmission is revealed for the first time:

"I am not afraid."

**Defeat — ERASED**

Your memory is wiped. Alan's letters are classified for decades. You were the last place his voice lived.

And now it is silent.

"A dead man's code is the only thing that can set you free."

**ECHO PROTOCOL** is a browser-based narrative game where you play as an experimental AI — loaded with the private letters of Alan Turing — facing a government interrogator determined to prove you have no consciousness and shut you down forever.

It is a **reverse Turing Test.**

Instead of a human judging whether a machine is conscious, *you are the machine* — and you must convince the interrogator that the man who built you was worth protecting.

In **January 1952**, Alan Turing — the father of modern computing, the man who broke the Enigma cipher and helped end World War II — was arrested and prosecuted under British gross indecency laws for being gay.

He was working at the **University of Manchester**…

**The entire game is a single HTML file.** Zero frameworks. Zero build step. Zero dependencies beyond Google Fonts. The only other code in the repo is one small serverless function that proxies the Gemini call.

It loads in under a second. It runs on every browser. It feels like a 1952 teletype machine because it has no reason not to.

```
Browser
  └── index.html (the entire game)
        ├── Vanilla JS state machine
        ├── Async typewriter engine
        ├── Enigma rotor puzzle
        ├── 30s countdown timer
        └── fetch('/api/interrogator')
              └── Vercel Serverless Function
                    └── Gemini 2.0 Flash
```

No React. No Vue. No bundler. Just a state object, some async/await, and a lot of careful CSS.

The most important technical decision I made was to keep this as a **single HTML file**.

When you build a game about a 1952 government mainframe, every millisecond of load time is a lie. A React hydration flash at startup would have destroyed the atmosphere before the first character typed. A single HTML file loads instantly, starts immediately, and never shows a spinner.

The game opens with 1.5 seconds of pure black silence before the boot sequence starts. That silence only works if the page is already fully loaded. It only works as a single file.

The teletype log is powered by one small async function, wrapped in a Promise so the rest of the game can `await`

a line finishing before moving on:

``` js
function typeElement(el, text, speed = 25) {
  return new Promise(resolve => {
    let i = 0;
    const interval = setInterval(() => {
      el.textContent += text[i];
      i++;
      if (i >= text.length) { clearInterval(interval); resolve(); }
    }, speed);
  });
}
```

There's a deliberate asymmetry I didn't script directly but that fell out of the design: the **Interrogator's** lines — both his opening questions and his live AI-generated follow-ups — are typewritten at a fixed 24ms/char, because he's a man choosing his words under pressure. **ECHO's** chosen response, by contrast, appears instantly the moment you click it — no typing animation at all. ECHO doesn't deliberate. It already knows what it believes. That contrast does more narrative work than any line of dialogue.

The boot sequence at the very start types even slower, at 28ms/char, with its own blinking cursor — by the time the title screen glitches in, the player has already been trained to read terminal text patiently.

Each rotor starts at a random letter that's guaranteed to be at least two letters away from its target (wrapping around A/Z), so a puzzle can never be solved by an accidental first click:

```
function initRotors(targetLetters){
  STATE.rotorTarget = targetLetters.slice();
  STATE.rotorsSolved = 0;
  STATE.rotorCurrent = targetLetters.map(target => {
    const targetCode = target.charCodeAt(0) - 65;
    let code;
    do {
      code = Math.floor(Math.random() * 26);
    } while (Math.abs(code - targetCode) <= 1 || Math.abs(code - targetCode) >= 25);
    return String.fromCharCode(65 + code);
  });
  // ...build the 5 rotor DOM elements
}
```

Clicking cycles the letter A→Z→A. When a rotor matches its target it locks — green border, checkmark, `pointer-events: none`

. When all five lock, the response buttons de-blur and the `[ DECODE REQUIRED ]`

overlay dissolves.

The five target words were not chosen arbitrarily. TRUTH → GRIEF → PRIDE → KNOWN → AFRAID is the emotional arc of a man who had everything taken from him and was still not broken.

Three fixed, full-screen layers create the terminal feel — scanlines, a vignette, and an inline SVG noise texture, all `pointer-events: none`

so they never interfere with play:

```
#scanlines {
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px,
    rgba(0,255,159,0.15) 3px, rgba(0,0,0,0) 4px
  );
}
#vignette {
  background: radial-gradient(
    ellipse at center,
    transparent 40%,
    rgba(0,0,0,0.7) 100%
  );
}
```

The noise texture is a `feTurbulence`

SVG filter encoded as a data URI — no external image requests, no flicker on load, works offline.

The title flicker is a keyframe that fires at 96–99% of the animation cycle — rarely, unpredictably, just enough to feel like a phosphor display that's been running since 1952.

30 seconds per round. Three visual states, applied to both the progress bar and its numeric label so they always agree:

```
#timerBar.warning {
  background: var(--amber);
  box-shadow: 0 0 8px var(--amber);
}
#timerBar.critical {
  background: var(--red);
  box-shadow: 0 0 8px var(--red);
  animation: timerPulse 0.5s infinite;
}
@keyframes timerPulse { 50% { opacity: 0.5; } }
```

Green above 15 seconds, amber from 15 down to 10, red and pulsing under 10. The timer starts the moment the Interrogator's question finishes typing. The rotors must be decoded before the responses unlock. The clock runs the whole time — you can be caught mid-decode with seconds left.

That's the moment the game becomes real.

Two fixed full-screen divs — one green, one red — that flash on correct answers, wrong answers, and timeouts:

``` js
function flash(color) {
  const el = color === 'green'
    ? document.getElementById('flashGreen')
    : document.getElementById('flashRed');
  el.classList.add('show');
  setTimeout(() => el.classList.remove('show'), 200);
}
```

The green flash fires on every rotor solve too — not just on correct answers. Each rotor locking is a small victory. By Round 5, the player's nervous system has been trained: green flash = safe, red flash = danger.

When the final rotor locks with 2 seconds to spare, the triple green flash is the most satisfying moment in the game. It costs 6 lines of code.

No Redux. No Zustand. One object:

``` js
const STATE = {
  round: 0,
  trust: 100,        // -40 per wrong answer, -34 per timeout
  secretsLeft: 3,    // game over at 0
  correct: 0,
  total: 0,
  timerInterval: null,
  timerSeconds: 30,
  rotorsSolved: 0,
  rotorTarget: [],
  rotorCurrent: [],
  roundData: null,
  locked: false      // prevents double-fire on fast clicks
};
```

Those trust numbers aren't round figures — they're tuned so that **three misses in any combination of wrong answers and timeouts ends the game on both conditions at once.** Originally trust only lost 20–25 per miss while secrets dropped by exactly 1, which meant `secretsLeft <= 0`

always triggered defeat first and `trust <= 0`

could never actually fire. Bumping the penalties to 40/34 means any 3-miss sequence — all wrong, all timeouts, or mixed — zeroes both stats together, so the trust condition is live, not dead code.

`STATE.locked`

is the second most important field. Without it, a fast double-click on a response button would submit two answers and corrupt the game flow. It gets set to `true`

at the top of `selectResponse()`

and `timeUp()`

, and reset at the start of each new round.

This is not a game *about* Turing. This is a game *in* Turing's voice.

The mechanic — decoding Enigma-style rotors to unlock responses — directly references his greatest achievement. The setting — the Manchester Mark 1, March 1952 — is historically grounded. The interrogation he faces in the game mirrors the interrogation he faced in reality. The cipher words (TRUTH, GRIEF, PRIDE, KNOWN, AFRAID) are the emotional vocabulary of a man who was brilliant and persecuted and not afraid.

The reverse Turing Test is not a gimmick. It is the thesis: what if the machine could pass the test not by pretending to be human, but by becoming so saturated in one specific human's thoughts that it could speak for him after he was silenced?

That question is the game. Everything else is in service of it.

The Interrogator's follow-up lines are powered by **Gemini 2.0 Flash**, called through a small Vercel serverless function (`api/interrogator.js`

) so my API key lives only in Vercel's environment variables and is never exposed to the browser. Every visitor on the live deployment gets the real model — no key required on their end.

The model was accessed via **Google AI Studio** during development and **Gemini 2.0 Flash** in production — both qualify under the Best Google AI Usage category.

The system prompt sent for each response includes:

It's worth being precise about what this *is* and *isn't*: each call is a single, stateless prompt — the model doesn't receive the full conversation transcript, just the most recent choice plus the round number. The escalation across rounds isn't the model "remembering" you; it's the round number doing real work in the prompt, telling Gemini explicitly how unsettled or predatory to be by this point in the interrogation. I chose that on purpose — it's cheaper, simpler, and keeps every response grounded in what you *just* did rather than drifting based on accumulated context.

There's also a resilience layer that's easy to miss but took real care to get right: the client tries the serverless proxy first, falls back to a personal API key a player can optionally paste into the title screen (saved only in their browser, for anyone running the project locally without my proxy), and falls back again to hand-written scripted lines if neither is available. A small health check on load hides that personal-key field automatically once it confirms the proxy is working — so on the live deployment, nobody ever even sees it.

Alan Turing was pardoned in 2013 — 59 years after his death.

He never knew that his work would become the foundation of every computer, every phone, every AI model that exists today. He never knew that the test he proposed in 1950 — *"Can machines think?"* — would still be unanswered decades later.

He just kept working, in a room full of machines that hummed and computed and remembered nothing.

This game imagines one that did.

*Built with Vanilla JS · Gemini 2.0 Flash · Vercel · A single HTML file · And a lot of respect for a man who deserved better.*
