I added a Yes or No page to my NameWheelPro project. It's vanilla JS with zero dependencies, so every fix below works in any project.
If you're building your own yes no wheel, here are four gotchas I'd want to know about up front.
- Streaks are normal, but they look like bugs
Spin a fair 50/50 wheel and sooner or later you'll see Yes, Yes, Yes, Yes, Yes. Users will assume it's rigged. Don't "fix" it by preventing repeats, because that makes the tool less random. Prove it instead:
function longestStreak(results) {
let best = 1, run = 1;
for (let i = 1; i < results.length; i++) {
run = results[i] === results[i - 1] ? run + 1 : 1;
best = Math.max(best, run);
}
return best;
}
const spins = Array.from({ length: 100 }, () => (randomInt(2) ? 'Yes' : 'No'));
console.log(longestStreak(spins)); // usually somewhere between 5 and 8
Run it a few times. Streaks of five or more show up in most 100-spin sessions, and that's what real randomness looks like.
- Holding the spacebar spins repeatedly
A keyboard shortcut is easy until someone holds the key down and the browser fires keydown over and over. Ignore repeats and keep a guard for double triggers:
document.addEventListener('keydown', (e) => {
if (e.code !== 'Space' || e.repeat) return; // ignore a held-down key
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement.tagName)) return;
e.preventDefault(); // no page scroll
if (!state.spinning) spin(); // second trigger becomes a no-op
});
- Show a tally so fairness is visible
A History list is good, but a running count is better because it shows the balance at a glance:
function tally(history) {
return history.reduce((t, h) => {
const key = h.result.trim().toLowerCase(); // users can edit entries, so normalize
t[key] = (t[key] || 0) + 1;
return t;
}, {});
}
tally(history); // { yes: 12, no: 9 }
A 12-to-9 split looks suspicious until you know that's normal variance. Showing both the streaks and the tally sets the right expectation.
- "Clear All" needs an undo
The default wheel is preloaded, so one accidental click on Clear All wipes it. A snapshot and a toast fix that in a few lines:
let undoSnapshot = null;
function clearAll() {
undoSnapshot = [...state.entries];
setState({ entries: [] });
showToast('Wheel cleared', 'Undo', () => setState({ entries: undoSnapshot }));
}
Destructive buttons should always have a way back.
Wrapping up
None of these is hard, but together they make a tiny tool feel trustworthy: honest about randomness, safe with keyboard input, transparent with results, and forgiving of mistakes.
You can try the live yes no wheel here, with no signup. What's a tiny UX detail that made one of your projects feel finished?
Top comments (0)