Flip a Coin Online — Heads or Tails Random Coin Toss
Need a fair coin flip without digging one out of your pocket? This online coin tosses heads or tails with a 600-millisecond CSS 3D animation, then records the result so you can settle a bet, pick who goes first, or run a quick statistical demo without losing track of the count. Each press of the button is an independent trial, so the cumulative heads/tails ratio drifts toward 50/50 the more you flip — useful when you want a visible demonstration of the law of large numbers rather than a single binary outcome.
About this tool
Each flip calls the browser's Web Crypto API (crypto.getRandomValues) and uses rejection sampling to pick 0 or 1 without modulo bias — the same entropy source modern password managers and TLS use. We deliberately avoided Math.random(), which is fast but not cryptographically random and can show subtle patterns over very long runs. The result is rendered with a CSS rotateY animation across 2520 degrees so the coin lands on the chosen face from the same starting position every time, and the tally + history persist for the current session so you can audit the streak after the fact. Common uses: settling a fair decision when no physical coin is available, kicking off a sports match online, demonstrating coin-toss probability to students, or sanity-checking that your team's tournament bracket draw was actually random.
- Cryptographically random outcome via Web Crypto (no Math.random bias)
- 600 ms 3D CSS rotateY animation, no JavaScript animation lib
- Running tally of heads vs tails for the current session
- Full chronological flip history (H/T sequence)
- Reset button to clear tally and start a new session
- Works offline once the page has loaded — no server round-trip per flip
- No accounts. Flip outcomes generated and stored only in your browser tab.
- Mobile-friendly tap target — works one-handed
- Keyboard-accessible: Space or Enter triggers a flip
- Suitable for tie-breakers, classroom demos, and unbiased decision making
Free. No signup. Your inputs stay in your browser. Ads via Google AdSense (consent required).
Frequently asked questions
Is the coin actually fair — really 50/50 every flip?
Yes, mathematically. Each flip calls `crypto.getRandomValues()` (W3C Web Cryptography API Recommendation 26 January 2017) and uses rejection sampling to map a uniform 32-bit integer to 0 or 1. Since 2 divides 2³² evenly, no modulo bias enters the result — every flip is an independent Bernoulli(0.5) trial. The 600-millisecond CSS rotateY animation is purely visual; the outcome is decided at the start of the click and only revealed after the animation. By contrast, physical coins are not perfectly 50/50: a Diaconis, Holmes & Montgomery 2007 mechanical analysis (SIAM Review 49(2):211–235) estimates a ~50.8%/49.2% bias toward the same side it started for the typical thumb-flip-and-catch (without bouncing); the Bartoš et al 2024 replication across 350,757 flips measured Pr(same side) = 0.508 within tight error bars. The virtual coin removes that physical-flip bias entirely.
Why does my run of 10 flips show 7 heads instead of 5?
Short runs do not converge. A fair coin's heads count over 10 flips follows a Binomial(10, 0.5) distribution: 5 heads is the most likely single outcome at 24.6%, but 7 heads happens about 11.7% of the time (and the symmetric 3-heads outcome another 11.7%). The expected absolute deviation from 5 heads is √(10 × 0.5 × 0.5) ≈ 1.58. Jakob Bernoulli's Ars Conjectandi (1713) proved the Law of Large Numbers: the observed heads/tails ratio converges in probability to 50/50 as the trial count grows, but "converges" allows arbitrarily long short-term deviations. Pearson and Weldon's 1900 dice-tossing dataset (26,306 throws of 12 dice) is the classic worked example — the chi-square goodness-of-fit test they introduced is what statisticians use to decide whether an observed frequency is consistent with a fair coin or rigged.
Could the result depend on when I clicked the button?
No. The randomness comes from `crypto.getRandomValues()`, which reads from the OS entropy pool (kernel-level, microsecond-resolution entropy from interrupts, hardware noise sources, and rdrand on x86 if available). Click timing might bias a `Math.random()` seeded from `Date.now()`, but `crypto.getRandomValues()` is not seeded by anything you can observe or influence from JavaScript. The same is true for keyboard input timing in the entropy pool: even if your click pattern affects the kernel pool slightly (which it does, by design, to mix in unpredictable input), the 256+ bits of accumulated entropy a typical OS holds make any single-user bias undetectable.
Why not just use Math.random() — would anyone notice?
For a one-off flip, no one would notice. For a long run, two issues appear: (1) statistical bias — V8's xorshift128+ (used since Chrome 49 stable in March 2016) passes BigCrush but not all linearity tests (Lemire & O'Neill 2018 arXiv:1810.05313 flagged structural failures in xorshift128+ and xoroshiro128+), and any user-visible streak that triggers "reset" + re-flip behaviour could be subtly biased by the periodic structure; (2) predictability — `Math.random()` state is recoverable from a few hundred outputs via Z3 SMT theorem-prover constraint solving (Lemire 2017 demonstration on xoroshiro128+, applies similarly to xorshift128+), which makes it unsuitable for any decision someone might want to game. `crypto.getRandomValues()` has none of these properties: the state is OS-internal, not exposed to script, and re-seeded continuously from hardware noise.
Why two ways to display heads vs tails — letters or words?
The single-letter rendering (H/T in English, C/X in Spanish) inside the coin face is for the animated coin itself, where space is limited and the letter needs to read as a typographic glyph at 2.5rem. The full word (Heads/Tails or Cara/Cruz) appears in the result label below the coin for clarity — it is the textual outcome that screen readers announce via the `aria-live="polite"` region per WCAG 2.1 SC 4.1.3 Status Messages (W3C Recommendation 5 June 2018), so non-sighted users hear "Heads" rather than spelling out the letter "H". The history strip uses letters again because it shows a chronological sequence and needs to fit many flips horizontally.
Sources (9)
- World Wide Web Consortium (W3C) (2017). Web Cryptography API — `crypto.getRandomValues()` plus rejection sampling pick 0 or 1 with exactly 50/50 probability per flip; same source modern password managers and TLS handshakes use; ECMA-262 `Math.random()` (V8 xorshift128+ since 4.9.41, Chrome 49 stable March 2016) is fast but not cryptographically random and is not the appropriate primitive for security-sensitive coin tosses. W3C Recommendation 26 January 2017 (Crypto.getRandomValues §3.3).
- Bernoulli, J. (1713). Ars Conjectandi (The Art of Conjecturing) — first formal proof of the Law of Large Numbers (Pars Quarta, the fourth part): as the number of independent Bernoulli trials grows, the observed proportion of successes converges in probability to the true success probability; for a fair coin this means the heads/tails ratio approaches 50/50 as the flip count grows, which is what the running tally on this page demonstrates flip by flip. Thurnisius, Basel, posthumous publication eight years after Jakob Bernoulli's 1705 death; ISBN 0-8018-8235-4 (Sylla 2006 Johns Hopkins University Press English translation).
- Pearson, K. (1900). On the criterion that a given system of deviations from the probable in the case of a correlated system of variables is such that it can be reasonably supposed to have arisen from random sampling — introduced the chi-square goodness-of-fit test, the canonical statistical procedure for asking "is this coin fair?" given an observed heads/tails frequency; Weldon's 26,306 dice tosses dataset analysed in this paper detected a small bias toward five and six on real wooden dice of the era. Philosophical Magazine, Series 5, Vol. 50, No. 302, pp. 157–175 (July 1900).
- Knuth, D. E. (1997). The Art of Computer Programming, Volume 2: Seminumerical Algorithms — Chapter 3 "Random Numbers" — formal treatment of pseudo-random number generation, including the modulo-bias proof: when the underlying RNG range (here, 2^32 from a Uint32Array) is not divisible by the desired bound (2 for heads/tails), naive `r % 2` is unbiased only because 2 divides 2^32 evenly; for non-divisor bounds rejection sampling is necessary. Addison-Wesley, 3rd edition (1997, ISBN 0-201-89684-2); Ch. 3 dates from 1st edition 1969.
- Diaconis, P., Holmes, S., & Montgomery, R. (2007). Dynamical Bias in the Coin Toss — mechanical analysis of the typical thumb-flip-and-catch coin toss shows a residual ~50.8 % bias toward the same side the coin started; the bias is dominated by the small wobble that keeps one side facing up slightly more than the other during the flight; subsequent replications (Bartoš et al 2024, 350,757 flips: Pr(same side) = 0.508) confirm the magnitude. SIAM Review 49(2) pp 211-235 (May 2007); DOI 10.1137/S0036144504446436.
- Lemire, D., & O'Neill, M. E. (2018). Xorshift1024*, Xorshift1024+, Xorshift128+ and Xoroshiro128+ Fail Statistical Tests for Linearity — found that the xorshift128+ algorithm V8 uses for `Math.random()` (since Chrome 49 / V8 v4.9.41) passes the BigCrush battery overall but fails specific linearity-detection tests; the failures are subtle and do not affect typical Monte-Carlo simulations but make these generators unsuitable for adversarial settings. Journal of Computational and Applied Mathematics 350 (April 2019) pp 86-94; pre-print arXiv:1810.05313 (October 2018).
- Lemire, D. (2017). Cracking random number generators (xoroshiro128+) — demonstrates that the internal state of xoroshiro128+ (and by extension the closely related xorshift128+ used by V8 `Math.random()`) can be recovered from a few hundred outputs via Z3 SMT theorem-prover constraint solving; the consequence is that any decision driven by `Math.random()` can be predicted by an adversary who observes enough draws. Personal blog at lemire.me, 22 August 2017; GitHub demonstration at github.com/lemire/crackingxoroshiro128plus.
- Vigna, S. (2014). Further scramblings of Marsaglia's xorshift generators — defines xorshift128+, the algorithm V8 (Chrome, Node.js, Edge, Opera) and SpiderMonkey (Firefox) use as the implementation of `Math.random()`; replaces the older MWC1616 that V8 used pre-v4.9.41. Journal of Computational and Applied Mathematics 315 (May 2017) pp 175-181; pre-print arXiv:1404.0390 (April 2014, last revised 2016).
- World Wide Web Consortium (W3C) (2018). Web Content Accessibility Guidelines (WCAG) 2.1 — Success Criterion 4.1.3 Status Messages — the flip result and running tally update via `aria-live="polite"` regions so screen reader users hear each outcome without losing keyboard focus on the flip button. W3C Recommendation 5 June 2018; carried unchanged into WCAG 2.2 (Recommendation 5 October 2023).
These are the original publications the formulas in this tool are based on. Locate them by journal name and year on Google Scholar or PubMed.
By Marco B. ·