DEV Community

Cover image for Building a Random Name Picker: 4 Small Problems, 4 Small Fixes
Devid E
Devid E

Posted on

Building a Random Name Picker: 4 Small Problems, 4 Small Fixes

I recently shipped NameWheelPro, a spinning wheel that picks a winner from a list of names. Building it looked easy, but the small details took most of the time. Things like duplicate entries, unreadable labels, and motion-sensitive users all needed handling.

If you've ever wanted to build your own random name picker, here are four problems I hit and how I solved them, with the code.

  1. Bulk import creates duplicates

Users paste lists from spreadsheets, chat threads, and class rosters. Duplicates and blank lines are guaranteed. Cleaning the input on import keeps the wheel tidy:

function importNames(text, existing) {
  const seen = new Set(existing.map((n) => n.toLowerCase()));
  const fresh = [];

  for (const raw of text.split(/[\n,]+/)) {
    const name = raw.trim();
    if (!name || seen.has(name.toLowerCase())) continue;
    seen.add(name.toLowerCase());
    fresh.push(name);
  }
  return [...existing, ...fresh];
}
Enter fullscreen mode Exit fullscreen mode

A Set gives O(1) lookups, and lowercasing catches "alice" vs "Alice".

  1. Four themes without four codebases

The wheel has four color themes: Rainbow, Pastel, Neon, and Slate. Instead of hardcoding colors in the drawing code, I store each palette as CSS custom properties and let the canvas read them:

:root[data-wheel="rainbow"] { --s1:#e8192c; --s2:#fdc835; --s3:#8cc924; --s4:#1a7fc4; }
:root[data-wheel="neon"]    { --s1:#ff00e5; --s2:#00f0ff; --s3:#39ff14; --s4:#ffea00; }
Enter fullscreen mode Exit fullscreen mode
function getPalette() {
  const css = getComputedStyle(document.documentElement);
  return [1, 2, 3, 4].map((i) => css.getPropertyValue(`--s${i}`).trim());
}
Enter fullscreen mode Exit fullscreen mode

Adding a fifth theme now means adding one CSS block, with no JavaScript changes.

  1. Trust, but verify the winner

After the animation ends, I don't rely on the animation math alone. I derive the winner from the wheel's final angle and check that it matches the intended result:

const TAU = Math.PI * 2;

function indexAtPointer(rotation, count) {
  const arc = TAU / count;
  const angle = ((-rotation % TAU) + TAU) % TAU; // pointer angle relative to wheel
  return Math.floor(angle / arc);
}
Enter fullscreen mode Exit fullscreen mode

If the two ever disagree, I know there's a bug in the animation code. It's a one-line safety net, and it's caught real off-by-one mistakes for me.

  1. Respect reduced-motion settings

A five-second spinning animation is fun for most people, but it can be uncomfortable for others. Browsers expose a media query for this, so I shorten the spin when it's set:

const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const spinDuration = reduceMotion ? 800 : 5000;
Enter fullscreen mode Exit fullscreen mode

The result is the same and the wheel is just as fair, but the animation is gentler. It took two lines and made the app noticeably more inclusive.

Wrapping up

None of these fixes is complicated, but together they're what separates a demo from something people actually use. If you're building a similar tool, start with data cleaning, theme tokens, a verification check, and accessibility.

You can try the live version at NameWheelPro. I'd love to hear what you'd add. What's the smallest detail that made one of your projects feel finished?

Top comments (0)