Behind the curtain
How the draw works
There is no server, no database and no Math.random() in the draw. The whole assignment is a
pure function of a fixed seed string, computed in the browser every time the page loads. Same seed → same
pseudo-random sequence → same shuffle → same Secret Santa. That is why everyone can spin as often as they like and
always land on the same name, and why every cousin's page agrees with everyone else's.
1. Seed → 53-bit hash (cyrb53)
The seed is a plain string. It is turned into a number with cyrb53, a small, public-domain non-cryptographic string hash by bryc. Change the seed and you get a completely different draw.
2. Hash → deterministic random numbers (mulberry32)
The hash seeds mulberry32, a tiny 32-bit pseudo-random number generator (PRNG) by
Tommy Ettinger. Given the same seed it always yields the identical sequence of numbers in [0, 1), in every browser,
because it only uses integer maths (Math.imul, shifts, XOR).
3. Random numbers → a shuffle (Fisher–Yates)
The givers stay in a fixed order. The list of receivers is shuffled with the Fisher–Yates (Knuth) shuffle, which produces every permutation with equal probability when fed uniform random numbers. Giver i is paired with shuffled receiver i.
4. Apply the family rules (rejection sampling)
A candidate shuffle is only accepted if every pair passes the rules. Each surname maps to a household; Bumma and Brakman share one household so they can never draw each other. Nobody can draw their own household, which also rules out drawing yourself. If a shuffle breaks a rule it is thrown away and the next numbers from the PRNG produce another candidate. Because the PRNG is deterministic, the same candidates are rejected in the same order every time, so the first valid draw is always the same one.
Only about 1.4% of random permutations satisfy the rules, so on average ~70 candidates are tried. With the current seed the first valid draw appears on attempt . The loop is capped at 100,000 attempts as a safety net.
5. Personal links
Each cousin's link carries an opaque token, ?santa=TOKEN. The token is
cyrb53(name + '|' + salt) written in base-36. On load the page looks the token up in the list of people, finds the
giver, then reads their receiver from the draw above. Names are never in the URL, so nobody accidentally spoils a friend's
result by tweaking the address bar.
6. Making the wheel land on the right name
The wheel is an inline SVG with ten 36° wedges. Alpine.js sets a rotate() style
and CSS transitions it over 6.5 s with an ease-out curve. The final angle is chosen so the pointer stops inside the
receiver's wedge: six full turns for drama, plus the angle to the wedge centre, plus a small offset (±13°) derived from the
token hash so it is the same offset every spin. Repeated spins add whole turns on top of the current angle, so the wheel always
spins forward and still stops on the same wedge.
Notes
- The only
Math.random()on the page positions the decorative snowflakes. It never touches the draw. - To reshuffle everyone, change the
SEEDconstant. To rotate the links without changing the draw, changeSALT. - This page is reached at
/code(or#/codeon hosts that don't rewrite paths). It is never linked from the main page. - Libraries: Tailwind CSS (styling), Alpine.js (state & transitions). Confetti is a ~40-line canvas animation, no library.