I added a random number page to my NameWheelPro project, and numbers turned out to be trickier than names. The whole thing is vanilla JS with zero dependencies, so every fix below works in any project.
If you're building your own pick a number wheel, here are four gotchas worth knowing, with the code.
- sort() puts 10 before 2
The default sort is alphabetical, even for numbers:
['1', '10', '2', '20', '3'].sort();
// ["1", "10", "2", "20", "3"], not what anyone wants
localeCompare has a numeric mode that fixes this and still handles text:
const sortEntries = (arr) =>
[...arr].sort((a, b) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' })
);
sortEntries(['10', '2', 'Bob', '1']); // ["1", "2", "10", "Bob"]
- Let people type ranges
If your tool accepts custom lists, nobody wants to type 1 through 50 one by one. Parsing 1-10, 15, 20-25 takes a few lines:
function parseCustom(text, max = 500) {
const out = [];
for (const part of text.split(/[\n,]+/)) {
const s = part.trim();
if (!s) continue;
const m = s.match(/^(\d+)\s*-\s*(\d+)$/);
if (m) {
let [a, b] = [Number(m[1]), Number(m[2])];
if (a > b) [a, b] = [b, a]; // "10-5" still works
for (let n = a; n <= b && out.length < max; n++) out.push(String(n));
} else if (out.length < max) {
out.push(s);
}
}
return out;
}
The max cap matters. Without it, someone typing 1-99999999 freezes the tab.
- Prove the randomness
crypto.getRandomValues is a good source, but random % 100 isn't perfectly uniform because 2³² doesn't divide evenly by 100. The bias is tiny, but rejection sampling removes it:
function randomInt(max) {
const limit = Math.floor(2 ** 32 / max) * max; // largest unbiased multiple
const buf = new Uint32Array(1);
do { crypto.getRandomValues(buf); } while (buf[0] >= limit);
return buf[0] % max;
}
Then test it instead of trusting it:
const counts = new Array(100).fill(0);
for (let i = 0; i < 1_000_000; i++) counts[randomInt(100)]++;
console.log(Math.min(...counts), Math.max(...counts));
// both should land roughly between 9,700 and 10,300
If one number wins far more often, you have a bug. It's a 10-second test that builds real confidence.
- Tell users when you drop input
Capping entries protects performance, but silently discarding the rest looks like a bug. Return how many were dropped so the UI can say so:
const MAX_ENTRIES = 500;
function addEntries(current, incoming) {
const room = Math.max(MAX_ENTRIES - current.length, 0);
const accepted = incoming.slice(0, room);
return {
entries: [...current, ...accepted],
dropped: incoming.length - accepted.length, // "12 entries were not added"
};
}
Wrapping up
None of these is hard, but together they make a number tool feel trustworthy: correct sorting, forgiving input, verified fairness, and honest feedback.
You can try the live pick a number wheel here, with no signup. What's the sneakiest "it works on my machine" bug you've hit with numbers?
Top comments (0)