SpinBloom URL API
Everything in SpinBloom is controllable by URL — no backend, no API keys, no rate limits. Build links that open a preloaded wheel, jump to a mode, share a ladder, reveal a Secret Santa match, or verify a fair draw. All state lives in the URL itself.
Query parameters
| param | what it does | example |
|---|---|---|
mode | Opens a tab: wheel · slots · ladder · bracket · teams · number · dice · santa · bingo | /?mode=bingo |
lang | UI language: en ko ja zh es fr de | /?lang=ko |
t | Loads a wheel template: yesno · food · nums · letters · consonants · truthdare | /?t=yesno&lang=en |
w | Preloads wheel entries — URL-safe base64 of a JSON array of {"n":name,"w":weight} | /?w=W3sibiI6IlllcyIsInciOjF9… |
l | A shared ladder (amidakuji): participants, results and the exact rung layout, restored masked | generated by the ladder's Share link button |
ss | A Secret Santa personal reveal: {"n":giver,"r":receiver} — shows a tap-to-reveal card for one person | generated by the Secret Santa draw |
proof | A self-verifying fair-draw proof: entries, seed, SHA-256 commitment and winners. The page recomputes everything and shows ✓/✗ | generated by fair mode's Copy proof link |
overlay | 1 = streamer mode: transparent background, wheel only, space-bar spins — for OBS browser sources | /?overlay=1 |
Parameters combine: /?overlay=1&w=… pins a specific wheel in a stream overlay; /?t=food&lang=ko opens the Korean food wheel.
Building a ?w= payload
The wheel payload is standard JSON, base64-encoded with URL-safe characters (+→-, /→_, padding stripped):
const entries = [
{ n: "Alice", w: 1 },
{ n: "Bob", w: 2 }, // weight 2 = twice the slice
{ n: "Carol", w: 1 }
];
const payload = btoa(unescape(encodeURIComponent(JSON.stringify(entries))))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const url = "https://spin.inblooms.net/?w=" + payload;
The same payload format drives the embeddable widget:
<iframe src="https://spin.inblooms.net/embed?w=PAYLOAD" width="420" height="540" style="border:0;border-radius:12px" loading="lazy" title="SpinBloom wheel"></iframe>
Verifying a ?proof= draw yourself
Fair-mode proofs are deliberately reproducible outside SpinBloom. The rule: picki = the first 8 bytes of SHA-256(seed + ":" + i) as a big-endian integer, mod the total weight of remaining entries, walked cumulatively over remaining entries in listed order (picked entries are removed between draws). In Python:
import hashlib, json, base64
payload = json.loads(base64.urlsafe_b64decode(PROOF_PARAM + "=="))
entries, seed = payload["e"], payload["s"]
assert hashlib.sha256(seed.encode()).hexdigest() == payload["c"] # commitment
remaining = list(range(len(entries)))
winners = []
for k in range(len(payload["w"])):
v = int(hashlib.sha256(f"{seed}:{k}".encode()).hexdigest()[:16], 16)
r = v % sum(entries[i]["w"] for i in remaining)
for j, i in enumerate(remaining):
r -= entries[i]["w"]
if r < 0:
winners.append(entries[i]["n"]); remaining.pop(j); break
assert winners == payload["w"] # reproduces the announced winners
Notes
All state is client-side — these URLs contain the data itself, nothing is fetched from a server, and opening them sends nothing back. Links have no expiry. The randomness behind every draw is crypto.getRandomValues. If you build something on top of this, we'd love to hear about it.