Introduction
Teachers, team leads, and raffle runners all need the same thing: pick a name, fairly, with no repeats until the pool is exhausted. This app does exactly that in about 70 lines — paste your roster, spin, and get a winner with a little animated suspense. It is the lightest app in this series, but it demonstrates fair randomness and pool management cleanly, complementing the password generator’s secrets lesson.
Features
- Roster input — paste names, one per line.
- Fair draws —
random.SystemRandom, cryptographically unbiased. - No-repeat mode — drawn names leave the pool until reset.
- Winner history — every draw logged with a timestamp.
- Suspense animation — a quick shuffle display before the reveal.
Prerequisites
- Python 3.8+ — from python.org.
- Streamlit — install with pip:
pip install streamlit
Step 1: Create the Script
Save as name_picker.py:
import streamlit as st
import random
from datetime import datetime
st.set_page_config(page_title="Random Name Picker", page_icon="🎯")
st.title("🎯 Random Name Picker")
secure_random = random.SystemRandom()
names_raw = st.text_area(
"Names (one per line)",
height=180,
placeholder="Aarav\nDiya\nKabir\nMeera\nRohan",
)
no_repeat = st.checkbox("No repeats until reset", value=True)
names = [n.strip() for n in names_raw.splitlines() if n.strip()]
if "pool" not in st.session_state:
st.session_state.pool = []
if "history" not in st.session_state:
st.session_state.history = []
# Reset pool when roster changes
if set(names) != set(st.session_state.pool) and not no_repeat:
st.session_state.pool = names.copy()
if no_repeat and (not st.session_state.pool or set(names) != set(st.session_state.pool + [n for n in st.session_state.history])):
st.session_state.pool = names.copy()
if st.button("🎯 Pick a winner", type="primary", disabled=len(st.session_state.pool) == 0):
placeholder = st.empty()
for _ in range(12):
placeholder.markdown(f"### 🎲 {secure_random.choice(st.session_state.pool)} ...")
time_waster = sum(range(1000)) # tiny pause between flashes
winner = secure_random.choice(st.session_state.pool)
placeholder.markdown(f"## 🏆 {winner}!")
st.session_state.history.append((winner, datetime.now().strftime("%H:%M:%S")))
if no_repeat:
st.session_state.pool.remove(winner)
if st.session_state.pool:
st.caption(f"{len(st.session_state.pool)} name(s) left in the pool")
if st.session_state.history:
st.subheader("Draw history")
for name, ts in reversed(st.session_state.history):
st.markdown(f"- **{name}** — {ts}")
if st.button("Reset everything"):
st.session_state.pool = names.copy()
st.session_state.history = []
st.rerun()
Step 2: Run the App
streamlit run name_picker.py
Paste a roster, hit pick, and watch the shuffle-then-reveal. Enable no-repeat mode for classroom use so every student gets a turn.
How It Works
Two ideas carry the app. First, fair randomness: random.SystemRandom draws from the OS entropy pool — the same source as the password generator’s secrets module. For a raffle this is overkill, but it costs one line and removes any doubt about bias.
Second, pool management: no-repeat mode keeps a pool list in session state and removes winners as they’re drawn, so the pool empties exactly once per roster. The reset logic watches for roster edits — if the set of pasted names changes, the pool rebuilds. This is the to-do list app’s load-modify-rerun cycle with a different payload.
The suspense animation is honest theater: flash twelve random names into an st.empty placeholder, then reveal the true winner. The sum(range(1000)) line is a deliberately tiny pause — a cheeky but effective trick for visible flashes without importing time.
Common Errors & Fixes
- Pool doesn’t reset after editing names — the set-comparison checks run in a specific order; keep both reset branches before the pick button.
- Winner appears in the pool again (no-repeat mode) —
pool.remove(winner)runs after the history append; if an exception fires in between, the removal is skipped — keep the remove immediately after the choice. - Animation doesn’t show — everything renders in the final rerun only; the flashes must target an
st.empty()placeholder created before the loop. - Blank names in the roster — blank lines and stray spaces; the
strip()+ truthiness filter handles both.
Key Concepts
SystemRandom— OS-entropy draws for provable fairness.- Pool depletion — remove-as-drawn guarantees full coverage.
- Roster diffing — rebuild state when inputs change meaningfully.
- Placeholder animation — rapid repaints of one
st.emptyregion.
What to Try Next
- Team splitter — shuffle the roster and deal names into N equal teams with
st.columns. - Add weights so some names appear more often (for classroom participation balancing, ironically).
- Persist history to CSV like the expense tracker for semester records.
- Add a prize wheel visual with
st.plotly_chartpie rotation.
FAQ
Is the animation influencing the result?
No — the winner is chosen after the flashes finish. The animation only displays random names; the actual draw is a single final choice call.
Why not just use Python’s random module?
For a classroom picker, plain random.choice is perfectly fair in practice. SystemRandom matters when unpredictability is a requirement — raffles with prizes, security draws — and it’s free to use.
Can I paste names from Excel?
Yes — copied cells arrive as newline-separated text, which is exactly the format the text area expects.