Coin Flip — CSS 3D Coin Flip Animation (JS)
Coin Flip · Games · Plain HTML, CSS & JS · Live preview
For supporters
Source Code
Want the source code?
Play the game for free. Become a WebDevPuneet supporter to unlock the complete HTML, CSS and JavaScript source code, the exports (HTML, React, Vue, Angular, Tailwind) and Fork.
Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Coin Flip — A 3D CSS Coin That Tumbles to Heads or Tails

Flipping a coin is the simplest possible randomiser, and a 3D coin that actually tumbles makes it satisfying. This snippet builds a two-sided coin that spins and lands on heads or tails, with a running tally, in pure HTML, CSS, and a few lines of vanilla JavaScript — no images, no library.
Two faces with backface-visibility
The coin is two circular faces stacked on the same spot inside a preserve-3d scene. The tails face is pre-rotated 180° so it points the opposite way, and both faces use backface-visibility: hidden — the key property that makes a flippable two-sided element work, because it hides whichever face is currently turned away from the viewer. Without it you'd see a mirror-image bleed-through; with it, exactly one side shows at a time as the coin rotates. The scene's perspective gives the spin real depth.
Landing on the right side via rotation parity
Heads shows when the coin's rotateY is at an even multiple of 180° (0°, 360°, …) and tails at an odd multiple (180°, 540°, …). A flip picks the result first with Math.random(), then computes a final angle that has the correct parity for that result plus several extra full spins so the coin visibly tumbles before settling. Because the landing angle is derived from the chosen outcome, the coin always rests on the side it reported — the randomness is honest, decided before the animation, never faked afterward. The Math.ceil((turns * 180) / 360) * 360 step is the part that keeps this correct flip after flip: it rounds the accumulated half-turns up to the next full 360°, discarding any leftover odd half-turn from the previous landing, before the new parity offset (0 or 180) is added back on top — without it, the parity from one flip could bleed into the next and land the coin on the wrong face.
Always spinning forward
An accumulating turns counter ensures each flip adds rotation on top of the last, so the coin keeps spinning in the same direction rather than snapping back to zero between flips. This continuity is a small detail that makes repeated flips feel like one continuous physical object rather than a resetting widget.
A running tally
Each result increments a heads/tails count, shown beneath the coin. Over many flips this both adds a sense of a session and quietly demonstrates that the 50/50 split holds — a nice touch for anything that uses the coin to make repeated decisions. The button disables during the ~1.1s tumble so flips can't overlap and desync the tally from the resting face.
Drop-in and adaptable
The coin is self-contained and easy to theme — swap the H/T glyphs for icons or images, change the metals, or resize it. Use it as a decision-maker, a game mechanic, or a playful "let fate decide" control, and as a clear reference for two-sided CSS 3D flips with backface-visibility.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA 3D coin renders on a dark stage with a Flip button.
- 2Flip itClick Flip (or the coin) and it tumbles for about a second, landing on heads or tails.
- 3Read the resultThe text shows the outcome, which always matches the resting face.
- 4Watch the tallyEach flip updates the running heads/tails count beneath the coin.
- 5Restyle itSwap the H/T glyphs for icons, change the coin colours, or resize it.
- 6Hook into your appRead the chosen side in flip() to drive a decision, game, or picker.
Real-world uses
Common Use Cases
Math.random() chooses the result before the spin so the animation always matches.backface-visibility, comparing with a 3D flip card for content cards.Got questions?
Frequently Asked Questions
Heads corresponds to an even multiple of 180° of rotateY and tails to an odd one. The flip picks the result with Math.random() first, then computes a final angle with the matching parity plus several full spins for flourish. Because the resting angle is derived from the chosen outcome, the coin always settles on the side it announced — the result is decided before the animation, not after.
It hides the side of each face that's turned away from the viewer. The coin is two faces on the same spot, with tails pre-rotated 180°. As the coin spins, backface-visibility:hidden ensures only the face pointing toward you is visible, so you never see the reverse bleeding through. It's the essential property for any two-sided 3D flip element.
A turns counter accumulates across flips, so each new flip's target angle is larger than the last. That keeps the rotation always increasing — the coin spins forward continuously instead of snapping back toward zero between flips, which would look like a glitch. The parity of the final angle still encodes heads or tails correctly.
Yes — Math.random() < 0.5 gives each side an equal chance on every independent flip. The running tally beneath the coin lets you watch the split converge toward 50/50 over many flips. There's no weighting or memory between flips, so it behaves like a fair coin.
In React, hold the rotation, result, and counts in state and set the coin's transform in the click handler with a timeout to settle; in Vue, bind :style with refs; in Angular, bind [style.transform] with component properties. The parity math and randomness are framework-agnostic — only the state and the transform binding move into the framework.