Tech With Galvan
Back to Articles
Development5/12/2025 • 3 min read

Random Name Picker using Python and Streamlit

Build a random name picker with Python and Streamlit — fair draws, no-repeat mode, winner history, and animated reveals for classrooms.

Galvan
Galvan

Founder & Creator

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.empty region.

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_chart pie 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.