How a draw works — a worked example

Walk through one real-shape draw end to end: 1,000 eligible comments, 3 winners, 9 reserves. No jargon, no hand-waving.

The scenario

Imagine the contest is over. Comments have been fetched, the contest's filters have run, and 1,000 entries are eligible. The prize awards 3 winners with 9 reserves, in case any of the winners can't be reached. Here is the per-entry probability — identical for every one of the 1,000 eligible comments, regardless of when it was posted or how long the commenter's name is.

Outcome Chance for any one entry
Drawn as a main winner (positions 1–3) 3 in 1,000 — 0.30%
Drawn as a reserve (positions 4–12) 9 in 1,000 — 0.90%
Drawn into any winning slot 12 in 1,000 — 1.20%

Nothing about this changes if the contest has 444 comments or 444,444. The algorithm is identical. Only the denominator moves.

Step 1 — Collect and filter the entries

The app fetches every comment on the Facebook or Instagram post you connected, then applies the contest's filters — duplicates, blocked words, account-age rules, whatever was configured. What survives is the eligible set. In our scenario that is 1,000 comments. Each one has a database row with a unique numeric ID; those 1,000 IDs are what the rest of the draw operates on.

Step 2 — Sort them into a canonical order

Before anything random happens, the app sorts the 1,000 IDs in strict ascending numeric order — 17, 142, 203, 1058, 9941, … — and discards the order they arrived in. Why? Because Facebook can return comments in different orders depending on pagination, time of day, or back-end changes. If the draw used Facebook's order, two re-fetches could produce different winners from the same random seed. Sorting first pins the input to one canonical list, so the seed alone determines the outcome.

Step 3 — Lock the input list with a SHA-256 hash

The sorted list is fed into SHA-256 — a cryptographic hash function — producing a 64-character fingerprint. The fingerprint, alongside the full list itself, is saved on the contest record. From this moment on, anyone inspecting the contest can confirm the eligible list hasn't been altered: re-hash the published list, compare to the saved fingerprint, and a single altered ID gives a completely different hash. There is no way to quietly add or remove a comment after this step without it being visible.

Step 4 — Generate a 256-bit random seed

The app asks the operating system's cryptographic random source (SecureRandom.hex(32)) for 32 bytes of unpredictable randomness, and stores the result as a 64-character hexadecimal string — for example a3f1…ce. 256 bits means 2²⁵⁶ ≈ 10⁷⁷ possible seeds. For perspective, the observable universe contains roughly 10⁸⁰ atoms. Nobody — not the contest creator, not the operator, not an attacker watching from outside — can predict or guess this seed before it is generated.

Step 5 — Shuffle the 1,000 IDs, then take the first 12

The seed initializes a deterministic shuffling engine, which then performs a Fisher–Yates shuffle on the 1,000 sorted IDs. Fisher–Yates is the standard fair-shuffle algorithm — every one of the 1,000 entries has an equal chance of ending up in every position. After the shuffle, the app takes the first 12 entries off the top. That is the entire selection step: shuffle, slice 12.

Step 6 — Assign positions

The 12 selected entries are walked in order. The first three become winners 1, 2, and 3, and the next nine become reserves 1 through 9. The remaining 988 eligible entries are recorded as not-selected. Comments that were filtered out earlier are marked as disqualified with the specific reason they didn't qualify.

Step 7 — Publish everything

Finally, the app saves five pieces of evidence on the contest record — all publicly readable. Without them, the result would be a black box. With them, anyone can reproduce the entire draw at home and confirm the published winners are the only winners the seed could have produced.

What gets published with every draw

Each of these is stored on the contest and exposed via the public verification pages. Together they make the draw fully reproducible — same inputs, same outputs, always.

  • The 256-bit random seed — the 64-character hex string from Step 4.
  • The sorted eligible-IDs list — all 1,000 IDs from Step 2, in canonical order.
  • The SHA-256 fingerprint of the IDs — the 64-character hash from Step 3. Confirms the published list hasn't been tampered with.
  • The algorithm version — currently v1. Pins the exact selection algorithm, so future improvements never retroactively change old results.
  • The Ruby version — pins the language runtime, since the shuffle's exact ordering depends on it.

Re-run the math yourself — in five lines

If you have Ruby installed (it ships with macOS and most Linux distributions), you can reproduce the exact draw at home. Each contest publishes its own eligible-ids.txt on its verification page (URL pattern: /results/<token>/verify) — the verification guide walks through downloading it step-by-step. Copy the published seed and paste this into irb:

require "digest"
seed = "PASTE_THE_PUBLISHED_SEED_HERE"
ids  = File.read("eligible-ids.txt").split("\n").map(&:to_i).sort

# (1) Sanity check the input: must equal the published SHA-256.
Digest::SHA256.hexdigest(ids.join("\n"))

# (2) Re-run the shuffle. First 3 items are winners 1..3 (in order);
#     the next 9 items are reserves 1..9.
ids.shuffle(random: Random.new(seed.to_i(16))).first(12)

The 12 IDs come out in the exact same order the contest published. Three winners, nine reserves, bit-for-bit identical. That is what makes a Pick a Winner draw verifiable instead of merely promised — the math does the work, not the operator's word.

Why this is hard to rig

  • The seed is unguessable. 2²⁵⁶ possibilities, generated only after the eligible list is committed. Nobody can pre-compute which IDs the seed would favor, because the seed does not exist until the moment of the draw.
  • The input is locked. The SHA-256 fingerprint means you cannot quietly add a comment from a friend after seeing the seed — the fingerprint would change and the draw would no longer reproduce.
  • The shuffle is mechanical. Once the seed and the sorted list exist, the winners are determined. No human decision sits between them. Any after-the-fact tweak would show up as a mismatch the moment anyone re-runs the math.
  • The receipt is public. Seed, list, hash, algorithm version, Ruby version — all five are published. There is no private piece of the draw that the operator could change without everyone being able to see.