How the Secret Santa draw works
Names are shuffled with a cryptographic random source, then a backtracking search gives every person a receiver who is neither themselves nor an excluded pair. If the rules leave no complete pairing, the site refuses to create the group and tells you why. Here is the code, the limits, and what you can check yourself.
What happens when you press Draw
Four steps run on the server before anything is saved. There is no second pass afterwards that silently drops a rule.
- Names are cleaned. Repeated spaces collapse, each name is cut to 40 characters, and duplicates are rejected case-insensitively with
Duplicate name: …. - Rules become a blocklist. Every exclusion pair is added in both directions, so "Ana and Ben" blocks Ana→Ben and Ben→Ana. An exclusion naming someone who is not in the list is ignored.
- Hopeless rules are caught before the search. If one person is excluded from every other participant, the draw stops and names that person.
- A randomised backtracking search builds one complete assignment. It returns the first full assignment it reaches; the group and its links are created from that result.
The code that decides the result
Randomness. The server does not use Math.random. It passes a generator built on the platform's cryptographic random API, and each call returns one of 4,294,967,296 values:
const crng = () => crypto.getRandomValues(new Uint32Array(1))[0] / 4294967296;
The shuffle is Fisher–Yates, walking from the last index down and swapping each item with a random earlier item or with itself:
for (let i = a.length - 1; i > 0; i--) {
const j = rnd(i + 1, rng);
[a[i], a[j]] = [a[j], a[i]];
}
The search places the people with the most exclusions first, because they have the fewest options, then tries receivers in shuffled order. A candidate is skipped when it is the giver, already used, or blocked:
if (r === g || used[r] || bad[g].has(r)) continue;
If a person runs out of candidates, the search backtracks to the previous person and tries their next option. Steps are capped at 200,000; if that budget runs out, or no complete assignment exists, draw() throws, the API answers HTTP 400, and no group is stored.
What the result guarantees
| Guarantee | How the code enforces it |
|---|---|
| Nobody draws themselves | The receiver is skipped when r === g |
| An excluded pair never matches, in either direction | Both directions go into bad, and bad[g].has(r) skips the pair |
| Every name is used exactly once | used[r] marks a receiver as taken; the result is a permutation |
| Group size stays between 3 and 100 | Checks throw before the search, not after; a Pro sign-up link raises the cap to 1,000 |
| Duplicate names cannot split a pairing | Case-insensitive duplicate check in cleanNames |
What "fair" means here
The code has no favourite. It does not read who created the group, who typed a name first, or where a name sits in the textarea: the search order comes from exclusion counts, ties are broken by a shuffle, and receivers are tried in shuffled order.
With no exclusions, every participant is equally likely to draw every other participant. With exclusions the guarantee changes: each returned pairing respects every rule, but the search returns the first complete assignment it reaches rather than sampling all valid assignments with equal probability. That is the honest description: random and constrained, not a uniform draw from the set of all valid matchings.
When exclusions make a draw impossible
The failure is explicit. Here is every message the draw can return, what triggers it, and the fix.
| Message | Trigger | Fix |
|---|---|---|
Add at least 3 people. | Fewer than three names | Add names, one per line |
Maximum 100 people. | More than 100 names in one draw | Split into teams: see large-group draws |
Duplicate name: Sam | Two entries match ignoring case | Make them unique, for example "Sam K." |
Sam is excluded from everyone. Remove a rule. | Sam is blocked from all other participants | Delete one of Sam's exclusion pairs |
No valid draw is possible with these rules. Remove an exclusion. | No complete assignment found, or the 200,000-step budget ran out | Remove one rule, or add a participant |
The message appears in the red alert line under the form, and nothing else happens: no group ID, no links, no half-finished draw. The names and rules stay in the form, so you can delete one pair and press Draw again.
Two different failures share the last message. The first is a rule set with no solution. In a group of four, if Ana and Ben are excluded from each other and both are also excluded from Cat, then Ana and Ben both have to draw Dan, which cannot work. The second is the search budget: a dense rule set can exhaust 200,000 steps even though a solution exists. In both cases removing one exclusion is the fastest fix; adding a person also relaxes the constraint.
Dutch and German pages show a translated version of the same message; the English page shows the string exactly as the server sends it.
What the organizer can and cannot see
| Data | Organizer page | Notes |
|---|---|---|
| Participant names | Yes | As entered |
| Each private link | Yes | One per participant |
| Link opened or not | Yes | viewed |
| Questionnaire answered | Yes | Only when a questionnaire was set (Pro) |
| Who draws whom | No | Not in the organizer response at all |
| Wishlists | No on the page | Exported in the Pro CSV |
| Photos | No | Shown only to the person who drew them |
Reveal is a separate action. It calls a different endpoint that returns the pair list and stamps revealedAt once — the first reveal wins. The organizer page then prints "The organizer revealed all results on <date> UTC". That is a timestamp, not an audit log: it does not record who looked, how often, or what they did with the list. Delete the group and the timestamp goes with it.
The Pro CSV contains name, private link, opened status, wishlist and questionnaire answers. It does not contain the pairings.
How to check your own link
- Open it. The page greets you by name and shows exactly one person, with the budget and that person's wishlist.
- Reload it. The same person appears, because the pairing is stored with the group rather than re-drawn on each visit. If the name changes, stop and tell the organizer.
- Change one character in the token and reload. You get "Link not found" (HTTP 404, marked noindex). The page is keyed to the exact token, not to a guessable sequence.
- Compare with the organizer's list. The organizer page contains one link per participant. Ask them to read yours out and compare it character by character.
- Read the URL. It has the shape
/r/<12-character group ID>-<16-character token>. The token comes from 8 random bytes, so 64 bits; the server compares it exactly and returns 404 for anything else.
The code does not de-duplicate tokens; it relies on 64 bits of randomness being enough that two participants never collide. That is a different guarantee from "checked unique", and it is the accurate one.
The limits of the check
There is no seed to re-run, no receipt, and no public audit log. The pairing lives in the group's storage until the group is deleted, and the only check available to a participant is consistency: the same link always shows the same one name, and the organizer's list has exactly one link per person. For a dispute, run the draw in front of the group and keep a copy of the link list before the reveal.
The rules that shape the draw — budget, date, reveal or not, what to do when someone drops out — are on Secret Santa rules. For a workplace exchange, the timeline and inclusion checklist are on office Secret Santa.
Source: the draw and organizer code on this site, checked 2026-10-30. Participant and pricing limits: Organizer Pro and how it works, checked 2026-10-30.
Questions
Is the Secret Santa draw really random?
Yes. The server shuffles with Fisher-Yates using a generator built on crypto.getRandomValues, not Math.random. There is no seed: nothing is stored that would let the same draw be repeated.
Can two people draw the same person?
No. Every receiver is marked as used when assigned, so the result is a permutation: each participant appears exactly once as a giver and exactly once as a receiver.
What happens if the exclusions make a draw impossible?
The draw stops with "No valid draw is possible with these rules. Remove an exclusion." and returns HTTP 400, so no group and no links are created. If one person is excluded from everyone, the message names that person instead. Removing one exclusion is usually enough.
Can the organizer see who drew whom?
Not by default. The organizer page returns names, private links and open status, but no pairings. Pressing Reveal all results returns the pair list and stamps the page with the date, which stays visible to the group.
Draw names now — it's free
Updated October 30, 2026