DEV Community

Jordan Vance
Jordan Vance

Posted on

What I learned shipping 5 name generators with zero dependencies

A weekend project got out of hand and I ended up shipping five small name generators. No framework, no backend, no npm install at runtime — five static HTML pages built from one Node script. Here are the parts that turned out to be harder than the idea.

The actual problem: "random" reads as garbage

My first cut for a D&D name generator just sampled random letters weighted by English frequency. The output was technically pronounceable and completely lifeless — Thrunak, Bli, Qwepor. Nobody looks at Qwepor and thinks "that's my half-orc."

What worked was dropping the per-letter model entirely and going to syllable pools per race: a leading sound, an optional middle, an ending. Elf endings lean on -iel / -wyn / -las, dwarven ones on hard stops like -grim / -dur. You are not modelling language, you are modelling the vibe a player already has in their head. Three hand-curated pools beat a clever Markov chain every time here, because the failure mode of a Markov chain (occasional unpronounceable sludge) is the one thing that breaks the illusion.

Constraints change the whole algorithm

The username generator has a different job: the output has to be available somewhere. Pretty names are worthless if the handle is taken on every platform. So the strategy flipped — instead of one clean word, it pairs two unrelated common words plus an optional short number suffix. Collision space goes up by orders of magnitude, and the result still reads as intentional rather than user82741.

The team name generator is the easy cousin: adjective + noun, bucketed by context (sports vs. work vs. trivia night). The only real lesson there was that "punchy" is mostly a length constraint. Two short words land; three-word names read as a committee wrote them.

Build-time, not run-time

Everything is generated at build time into static files — one build.mjs reads the generator definitions and stamps out each page. No client framework. The "generate" button is ~30 lines of vanilla JS that samples the pools embedded in the page. Tap-to-copy on each result, because the universal next action after generating a name is copying it, and making someone highlight-drag on mobile is hostile.

Total runtime dependencies: zero. The whole hub and the five tools load instantly because there is nothing to hydrate.

What I'd tell past me

  • Curated pools > statistical models when the output is judged on taste, not correctness.
  • Decide what "good output" means before you pick an algorithm. "Pronounceable", "available", and "punchy" are three different optimisation targets and they led to three different designs.
  • Static build steps are underrated for this kind of thing. No cold starts, no bill, trivially cacheable.

If you want to poke at the output, the D&D one is the one I spent the most time tuning. Happy to talk through the syllable-pool approach if anyone's building something similar.

Top comments (0)