Coin Flip Web App Made using JavaScript, HTML and CSS animations

During one of our class discussions, we talked about the functionality that would be involved in a program that simulates the flipping of a coin. Under the original circumstances, we needed a button that once clicked our program runs through an array of turns and produces a random outcome (either one and two) for each iteration. When you finish iterating over the array of turns, you will have an array full of ones and twos. If there were more ones, then the JavaScript would print out that ‘Heads’ was the winner. If there were more twos in the array, then ‘Tails’ would be the winner. I found this to be a riveting discussion topic. SO interesting in fact, that I have spent over a week building out my very own coin flipping web app. Is your mind blown yet?

OK, Sophia. Maybe you don’t find the initial concept interesting, but I don’t find you interesting! You are the least interesting of all the Golden Girls. All you do is nag and bring down the other more entertaining Golden Girls who are just trying to live their lives. Betty White for life!

Where was I? Oh, yes, the exciting world of coin flipping.

I can’t say I blame Sophia for not finding the original design interesting. Here were a few problems I had with it:

  1. There was no active record of wins
  2. The program ran through all the turns at once
  3. Very little interaction, aside from clicking a button and seeing a message
  4. Minimal celebration of victory

It’s true. In the original design, you clicked a button (yay?) and then boom ‘x’ number of turns are finished instantaneously and there’s a winner. It worked…but that was it? So after figuring out the initial functionality, I thought I’d keep going and make it so the users could go one turn at a time. I was able to achieve this by separating the the flipping functionality and the turn iteration into two separate methods:

function coinFlip() {
function flip(){
return Math.floor((Math.random() * 2) + 1)
}
var result = flip();
if (result === 1){
document.getElementById("coin").src="images/heads.png";
winner = `HEADS`;
head_win = heads_wins.push(result);
document.getElementById("head_win").innerText = head_win;
} else if (result === 2) {
document.getElementById("coin").src="images/tails.png";
winner = `TAILS`;
tail_win = `${tails_wins.push(result)}`;
document.getElementById("tail_win").innerText = tail_win;
}
document.getElementById("winner").innerText = winner;
}

The coinFlip() method handles the actual flipping of the coin, as well as printing out which side of the coin prevailed. The user clicks an image of a quarter, and the onclick event handler makes the image spin. flip() controls the random numerical outcome. If the result of flip() is 1, coinFlip() prints HEADS and displays the heads side of a quarter. If it’s 2, coinFlip() prints TAILS and displays the tails side of the coin. The outcomes are stored in two arrays: heads_wins stores all the 1 outcomes and tails_wins stores all the 2 outcomes. coinFlip() also prints the current length of each array after the coin is flipped. These values can be seen in the score box which updates after every turn. Then we have a method that keeps score:

function score(){
if (heads_wins.length + tails_wins.length === 9){
if (heads_wins.length > tails_wins.length){
document.getElementById("coin").src="images/heads.png";
final_winner = `The winner is HEADS with ${heads_wins.length} wins!`;
print_winner();
} else if (tails_wins.length > heads_wins.length) {
document.getElementById("coin").src="images/tails.png";
final_winner = `The winner is TAILS with ${tails_wins.length} wins!`;
print_winner();
}
}
}

This stops the game after an odd number of turns (9) to ensure there is no tie. if the heads_wins.length is longer than tails_wins.length, score() evaluates HEADS as the winner. And if tails_wins.length is larger, then TAILS is the winner. Then there is another method that prints the winner, aptly named print_winner().

function print_winner(){
winner = ''
document.getElementById("final_winner").innerHTML = final_winner;
fallingCoins()
}

I guess I could have stopped there. You have a winner, a message flashes, it’s all fine and dandy. But Sophia is still not pleased. What else is new…

Wow. Harsh. What does Betty White have to say about all this?

There we go. Much better. Betty the babe is always a beacon of light and positivity. So I guess she’s OK with me stopping here. But then I thought, this is JavaScript. Let’s have some fun with it! Why not add a more profound finale to this rousing game of coin flipping. Come on. WE’RE FLIPPING A FRICKEN’ COIN!!!

So I thought, what would Betty do? Well, Betty, being the superstar she is, would make it rain. But my funds have been running a little low lately.

So we’ll make it rain quarters! How do we do that? Allow me to introduce fallingCoins():

function fallingCoins(){
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
if (heads_wins.length > tails_wins.length){
document.getElementById("damn").innerHTML += "<div id='coin_fall'><img id='falling_coin' src='images/heads.png'/></div>"
} else {
document.getElementById("damn").innerHTML += "<div id='coin_fall'><img id='falling_coin' src='images/tails.png'/></div>"
}
// true means clone all childNodes and all event handlers
for (var i = 0; i < arr.length; i++) {
var rand = Math.floor(Math.random() * 3) + 2;
setTimeout(addCoin, (i * rand) * 300);
}
}

I made an array (1–10) which represents the number of coins I want to generate.If the winner is heads, it will rain heads coins. If tails wins, then it will rain tails (I would like to get it so that it could rotate and show the front and back of the quarter simultaneously but that’s an undertaking for later on). Once the winner is determined, I added a coin_fall div, which has the child image, failling_coin. It is injected in the damn div. (I was frustrated when I wrote that). Originally I had those lines in the original html. But then I would have a random quarter just falling over and over again at the left of the screen. I only wanted to add it when someone wins. While iterating through the array, setTimeout is used to call a function at random times (but the range isn’t too crazy, that way we’re not waiting 5 minutes for coins). The function, addCoin() is where the magic happens.

function addCoin() {
var coin_div = document.getElementById('coin_fall')
var rand = Math.floor(Math.random() * 1000) + 1
coin_div.style.position = "fixed";
coin_div.style.top = '-120px';
coin_div.style.left = rand +'px';
var clone = coin_div.cloneNode(true);
document.body.appendChild(clone);
}
@keyframes coin_fall {
0% {
transform: translate(0px, -20%);
} 100% {
transform: translate(0px, 1000%);
}
}
@keyframes infinite-spinning {
from {
transform: rotateX(0deg);
} to {
transform: rotateX(720deg);
}
}

I originally added coin_fall in this function, but I didn’t need to add this line AND create a new coin every time we iterate through the 10 elements of the previous array. So I just wrote that line once in fallingCoins(). But we grab the newly added elements here in addCoin(), and store that value as coin_div. Once the elements are added, we generate another random number. This random number will be used to determine a random point from the left of the screen (approximately between 0px and 1000px).

Now add styling to the coin_div, which includes a top parameter(starting point). A fixed position of -120px is good because coin_div will start the fall from the top of the page and out of view. Then the left positioning is a random number between 0 and 1000 so it can appear anywhere on the screen. After the styling is added, coin_div is cloned with am true value. That way the original element as well as any children and event listeners/animations (the child in this case being falling_coin) are cloned.

Since you can’t apply two animations to one element, I had to apply one animation to the img and the other to the coin_fall div. See below.

@keyframes coin_fall {
0% {
transform: translate(0px, -20%);
} 100% {
transform: translate(0px, 1000%);
}
}
@keyframes infinite-spinning {
from {
transform: rotateX(0deg);
} to {
transform: rotateX(720deg);
}
}

Translate is a css transformation that changes the position of the element in the window. RotateX is used to rotate the element in place on its X access (up and down). A full rotation is 720 degrees. And for styling the div and img, I set the animation to occur over the 4 seconds, that way it moves slowly enough for you to actually see and enjoy the animation.

#coin_fall{
animation: coin_fall 4s infinite;
position: fixed;
}
#falling_coin {
width: 100px;
animation: infinite-spinning 4s infinite;
margin: 0;
}

One roadblock I encountered was when I was trying to get the coin to flip when the user presses it. The problem: css doesn’t recognize onclick as a selector like it would hover. Instead, I utilized active which executes the animation so long as the user is holding down the click.

input:active{
transform: rotateX(1800deg) scale(4);
}

But we can’t expect the user to hold down their click on the image, wait for the animation to finish and then release in order to see the outcome. To get around this, I made the rotation a high number (but made sure to keep it a multiple of 360 so it would complete on a full spin), and set the transition to half a second. This way, you accomplish more of the full animation in a fraction of the time it takes to click.

input {
margin-left: 42.5%;
margin-right: 42.5%;
width: 15%;
transition: all .5s linear;
}

Thus simulating a flipping animation that occurs onclick.

So here it is, Heads or Tails. The moment Betty White has been waiting for and Sophia has been loathing.

Viola! We have ourselves a pretty neat coin flipping web app. You may have noticed however, that some of the falling coins are glitchy. Sometimes they disappear before they reach the bottom of the page. Other times they continue heading down the page but randomly change course and jump to another part of the screen. Sometimes it looks like the coins are generated not -120px off screen, but in the middle of the page. I have a hypothesis:

When I look at the execution in console, you can clearly see there are a few different places where coin_div is added to the html:

So it looks like the creation and positioning is working fine, the top is constant and then the left pixel value is a random number between 0 and 1000. But you see, there is that one value that gets added under damn and the rest are prepended to the body of the webpage, above the damn div. I’ve tried doing one or the other but it only either produces one new element or produces a bunch but they produce rapid fire and is doesn’t look like seamless generation.

I am also curious if it has something to do with the css styling. Maybe the divs or the images are impinging on space and ultimately push each other to the side. Or maybe the margins are overwhelmed, resulting in mismatched starting/ending points. I have been able to overcome almost every bug that I have ran into. And I will figure this one out too. But for now, I just can’t look at it anymore. This…this code…has become the bane of my programming existence. The Mozart to my Salieri. But it wasn’t the code that was mocking me. It was God!

For the source code, please head over to the GitHub repo.

If you enjoyed the post or need clarification on anything mentioned, let me know in the comments. Feel free to share and leave claps!

References

Code 📲💻 Wellness 🧘🏻‍♂️😌

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store