DEV Community

Jean Michael Mayer
Jean Michael Mayer

Posted on • Originally published at dragons-dictionary.edgecasefactory.com

Show Dev: A Dragon That Burns Your Village If You Misspell

The pitch

I built The Dragon's Dictionary: a bilingual (English/Portuguese) word game where a hoarding dragon eats words you feed it. Spell them right, the dragon is pleased and adds them to its hoard. Spell them wrong, and it torches your village.

It started as a joke about how Duolingo's owl is menacing but polite. I wanted something that felt genuinely unhinged โ€” a creature that cares more about its word collection than your feelings.

Why a dragon, why two languages

I'm learning Portuguese. Most vocab apps treat bilingual learners like monolinguals who happen to own a dictionary. I wanted a game where switching languages mid-session is the point, not a settings toggle buried three menus deep.

The dragon also solves a UX problem I keep hitting with language apps: stakes. Flashcards have no stakes. A burning village has stakes. The cognitive load of "don't kill the villagers" turns out to be a surprisingly effective mnemonic device.

The weird technical choices

It's almost entirely AI-generated. Not "AI-assisted" โ€” I mean the dragon's reactions, the villager names, the insults it hurls when you fail, are generated on demand. I keep a small corpus of base words and let the model riff on reactions per session. This is why the dragon feels inconsistent in a charming way: it is inconsistent.

It runs in its own Railway service. I have a small constellation of side projects under edgecasefactory.com, and I used to cram them all into one monorepo behind one Next.js deployment. That was a mistake. One project's dependency bump would break two others. Now each dragon, factory, and whatever-else gets its own Railway service with its own lifecycle. Deploys take 40 seconds and I stopped dreading npm update.

No database for game state. The dragon's hoard lives in localStorage. I considered Postgres, Redis, SQLite-on-a-volume, the works. Then I remembered this is a game about a dragon and nobody needs their village-burning history to sync across devices. Deleting the database was the single best architectural decision I made.

The scoring loop

Here's the core validation call. The model grades the word, returns a reaction, and decides the dragon's mood:

async function feedDragon(word, lang) {
  const res = await fetch('/api/feed', {
    method: 'POST',
    body: JSON.stringify({ word, lang, hoard: getHoard() })
  });

  const { valid, reaction, mood, villagersLost } = await res.json();

  if (valid) {
    addToHoard(word);
  } else {
    burnVillage(villagersLost);
  }

  return { reaction, mood };
}
Enter fullscreen mode Exit fullscreen mode

The server-side check does two things: a dictionary lookup (cheap, deterministic) and an LLM call for the reaction text (expensive, flavorful). If the LLM times out, the dragon just grunts. Graceful degradation, dragon-style.

What surprised me

Portuguese has a lot of accented characters that are easy to get wrong on a US keyboard. I ended up adding a "close enough" mode because forcing learners to type รงรฃo correctly on first try felt more like a bureaucratic form than a game. The dragon now accepts cao but makes fun of you for it.

Also: players burn their own village on purpose. A lot. I did not design for this. I now embrace it.

Try it

๐Ÿ‰ dragons-dictionary.edgecasefactory.com

Feed the dragon. Don't feed it gibberish. Or do โ€” the villagers signed waivers.

Feedback welcome, especially from Portuguese speakers who want to tell me my conjugation tables are wrong.

Top comments (0)