Random Name Picker
Fairly select students from your class list with a fun shuffling animation.
Names are automatically saved to your browser for each group.
0 names loaded. Click to edit
The winner is...
Embed This Tool
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Name Picker</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f0fdfa; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 20px; }
.container { max-width: 600px; width: 100%; background: white; border-radius: 16px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); padding: 32px; }
h1 { color: #0f766e; text-align: center; margin-bottom: 24px; font-size: 2rem; }
textarea { width: 100%; padding: 16px; border: 2px solid #ccfbf1; border-radius: 8px; font-size: 16px; font-family: inherit; resize: vertical; min-height: 200px; }
textarea:focus { outline: none; border-color: #14b8a6; }
.options { margin: 16px 0; display: flex; align-items: center; }
.options input { width: 20px; height: 20px; margin-right: 8px; }
.buttons { display: flex; gap: 12px; margin: 24px 0; }
button { flex: 1; padding: 16px 24px; border: none; border-radius: 8px; font-size: 18px; font-weight: bold; cursor: pointer; transition: all 0.2s; }
#pickButton { background: #14b8a6; color: white; }
#pickButton:hover { background: #0d9488; }
#resetButton { background: #e5e7eb; color: #374151; }
#resetButton:hover { background: #d1d5db; }
#winnerDisplay { display: none; margin-top: 24px; background: linear-gradient(135deg, #14b8a6, #0d9488); border-radius: 12px; padding: 32px; text-align: center; }
#winnerName { color: white; font-size: 3rem; font-weight: bold; }
.winner-animation { animation: winnerPop 0.5s ease-out; }
@keyframes winnerPop { 0% { transform: scale(0.5); opacity: 0; } 50% { transform: scale(1.1); } 100% { transform: scale(1); opacity: 1; } }
.shuffling { animation: shuffle 0.1s ease-in-out infinite; }
@keyframes shuffle { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
#remainingCount { text-align: center; color: #6b7280; margin-top: 16px; }
</style>
</head>
<body>
<div class="container">
<h1>Random Name Picker</h1>
<div style="margin-bottom: 16px;">
<label style="font-weight: 600; color: #374151; margin-bottom: 8px; display: block;">Student Group</label>
<div style="display: flex; gap: 8px;">
<select id="groupSelect" style="flex: 1; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; font-size: 16px;">
<option value="default">Default Group</option>
</select>
<button id="addGroupBtn" style="padding: 12px 16px; border: none; border-radius: 8px; background: #e5e7eb; color: #374151; font-weight: 600; cursor: pointer;">+ Add</button>
<button id="deleteGroupBtn" style="padding: 12px 16px; border: none; border-radius: 8px; background: #fecaca; color: #dc2626; font-weight: 600; cursor: pointer;">Delete</button>
</div>
</div>
<div style="margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<label style="font-weight: 600; color: #374151;">Student Names (one per line)</label>
<button id="toggleNamesBtn" style="background: none; border: none; color: #0d9488; font-size: 14px; font-weight: 600; cursor: pointer;">▼ Minimize</button>
</div>
<div id="namesContainer">
<textarea id="nameList" placeholder="Enter student names, one per line..."></textarea>
<p style="color: #6b7280; font-size: 14px; margin-top: 8px;">Names are automatically saved to your browser for each group.</p>
</div>
<div id="namesSummary" style="display: none; background: #f9fafb; border: 2px solid #e5e7eb; border-radius: 8px; padding: 16px;">
<p style="color: #374151;"><span id="namesCount">0</span> names loaded. <span id="expandNamesBtn" style="color: #0d9488; cursor: pointer; text-decoration: underline;">Click to edit</span></p>
</div>
</div>
<div class="options">
<input type="checkbox" id="removeAfterPick">
<label for="removeAfterPick">Remove name after picking</label>
</div>
<div class="buttons">
<button id="pickButton">Pick a Name!</button>
<button id="resetButton">Reset List</button>
<button id="restoreButton">Restore Original</button>
</div>
<div id="winnerDisplay">
<p style="color: white; font-size: 1.2rem; margin-bottom: 8px;">The winner is...</p>
<h2 id="winnerName"></h2>
</div>
<div id="remainingCount"><span id="count">0</span> names remaining</div>
</div>
<script>
const nameList = document.getElementById('nameList');
const pickButton = document.getElementById('pickButton');
const resetButton = document.getElementById('resetButton');
const restoreButton = document.getElementById('restoreButton');
const groupSelect = document.getElementById('groupSelect');
const addGroupBtn = document.getElementById('addGroupBtn');
const deleteGroupBtn = document.getElementById('deleteGroupBtn');
const winnerDisplay = document.getElementById('winnerDisplay');
const winnerName = document.getElementById('winnerName');
const removeAfterPick = document.getElementById('removeAfterPick');
const remainingCount = document.getElementById('remainingCount');
const countSpan = document.getElementById('count');
// Group management
let currentGroup = 'default';
let groups = { 'default': { names: '', original: '' } };
// Load groups from localStorage
const savedGroups = localStorage.getItem('namePickerGroups');
if (savedGroups) {
groups = JSON.parse(savedGroups);
}
// Load saved checkbox state
const savedRemove = localStorage.getItem('namePickerRemove');
if (savedRemove === 'true') {
removeAfterPick.checked = true;
}
// Save checkbox state
removeAfterPick.addEventListener('change', () => {
localStorage.setItem('namePickerRemove', removeAfterPick.checked);
});
function populateGroupSelect() {
groupSelect.innerHTML = '';
Object.keys(groups).forEach(groupName => {
const option = document.createElement('option');
option.value = groupName;
option.textContent = groupName === 'default' ? 'Default Group' : groupName;
groupSelect.appendChild(option);
});
groupSelect.value = currentGroup;
}
function loadGroupNames() {
if (groups[currentGroup]) {
nameList.value = groups[currentGroup].names || '';
}
updateCount();
}
function saveGroups() {
localStorage.setItem('namePickerGroups', JSON.stringify(groups));
}
populateGroupSelect();
loadGroupNames();
// Group selection change
groupSelect.addEventListener('change', (e) => {
currentGroup = e.target.value;
loadGroupNames();
});
// Add new group
addGroupBtn.addEventListener('click', () => {
const groupName = prompt('Enter group name:');
if (groupName && groupName.trim() && !groups[groupName.trim()]) {
const newGroup = groupName.trim();
groups[newGroup] = { names: '', original: '' };
currentGroup = newGroup;
populateGroupSelect();
loadGroupNames();
saveGroups();
} else if (groups[groupName.trim()]) {
alert('Group name already exists.');
}
});
// Delete group
deleteGroupBtn.addEventListener('click', () => {
if (currentGroup === 'default') {
alert('Cannot delete the default group.');
return;
}
if (confirm(`Are you sure you want to delete "${currentGroup}"?`)) {
delete groups[currentGroup];
currentGroup = 'default';
populateGroupSelect();
loadGroupNames();
saveGroups();
}
});
// Save names to localStorage on change
nameList.addEventListener('input', () => {
if (!groups[currentGroup]) {
groups[currentGroup] = { names: '', original: '' };
}
groups[currentGroup].names = nameList.value;
// Save as original if no original exists yet
if (!groups[currentGroup].original) {
groups[currentGroup].original = nameList.value;
}
saveGroups();
});
function getNames() {
return nameList.value.split('\n').map(name => name.trim()).filter(name => name.length > 0);
}
function updateCount() {
const names = getNames();
countSpan.textContent = names.length;
document.getElementById('namesCount').textContent = names.length;
if (names.length > 0) {
remainingCount.classList.remove('hidden');
} else {
remainingCount.classList.add('hidden');
}
}
// Toggle names container
document.getElementById('toggleNamesBtn').addEventListener('click', () => {
const container = document.getElementById('namesContainer');
const summary = document.getElementById('namesSummary');
const btn = document.getElementById('toggleNamesBtn');
if (container.classList.contains('hidden')) {
container.classList.remove('hidden');
summary.classList.add('hidden');
btn.textContent = '▼ Minimize';
} else {
container.classList.add('hidden');
summary.classList.remove('hidden');
btn.textContent = '▲ Expand';
}
});
document.getElementById('expandNamesBtn').addEventListener('click', () => {
const container = document.getElementById('namesContainer');
const summary = document.getElementById('namesSummary');
const btn = document.getElementById('toggleNamesBtn');
container.classList.remove('hidden');
summary.classList.add('hidden');
btn.textContent = '▼ Minimize';
});
updateCount();
pickButton.addEventListener('click', () => {
const names = getNames();
if (names.length === 0) {
alert('Please enter at least one name!');
return;
}
winnerDisplay.classList.remove('hidden');
winnerName.classList.add('shuffling');
winnerName.textContent = names[0];
let shuffleCount = 0;
const maxShuffles = 20;
const shuffleInterval = setInterval(() => {
winnerName.textContent = names[Math.floor(Math.random() * names.length)];
shuffleCount++;
if (shuffleCount >= maxShuffles) {
clearInterval(shuffleInterval);
winnerName.classList.remove('shuffling');
const winner = names[Math.floor(Math.random() * names.length)];
winnerName.textContent = winner;
winnerName.classList.add('winner-animation');
if (removeAfterPick.checked) {
const updatedNames = names.filter(name => name !== winner);
nameList.value = updatedNames.join('\n');
groups[currentGroup].names = nameList.value;
saveGroups();
updateCount();
}
}
}, 100);
});
resetButton.addEventListener('click', () => {
if (confirm('Are you sure you want to reset the name list?')) {
nameList.value = '';
groups[currentGroup].names = '';
saveGroups();
winnerDisplay.classList.add('hidden');
updateCount();
}
});
restoreButton.addEventListener('click', () => {
if (groups[currentGroup] && groups[currentGroup].original) {
nameList.value = groups[currentGroup].original;
groups[currentGroup].names = groups[currentGroup].original;
saveGroups();
winnerDisplay.classList.add('hidden');
updateCount();
} else {
alert('No original list saved for this group. Please enter names first.');
}
});
</script>
</body>
</html>