JSFiddle - React, Tailwind, and code Playground

by Kofod

HTML

<div id="coasterGame">
  <canvas id="coasterCanvas" width="960" height="650"></canvas>

  <div id="coasterHud">
    <span>🎟 Tickets: <b id="ticketScore">0</b></span>
    <span>❤️ Lives: <b id="lifeScore" >3</b></span>
  </div>

  <div id="coasterOverlay">
    <div class="coasterCard">
      <h1>🎢 Carnival Coaster</h1>
      <p>
        Don't hit the clowns.<br>
        Collect tickets, but do not fall through the gaps!
      </p>
      <button id="coasterStart">Start ride</button>
    </div>
  </div>
</div>

CSS

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f5edf3;
  font-family: Arial, Geneva, sans-serif;
}

#coasterGame {
  width: 960px;
  height: 650px;
  position: relative;
  overflow: hidden;
  margin: 0 auto;
  background: #ffe2ec;
  font-family: Arial, Geneva, sans-serif;
  box-shadow: 0 8px 20px rgba(90, 45, 70, 0.25);
}

#coasterCanvas {
  width: 960px;
  height: 650px;
  display: block;
}

#coasterHud {
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  z-index: 5;
  display: flex;
  justify-content: space-between;
  pointer-events: none;
}

#coasterHud span {
  background: rgba(255, 255, 255, 0.82);
  border: 2px solid #ff9abb;
  color: #6b2844;
  padding: 7px 12px;
  border-radius: 999px;
  font-weight: bold;
  font-size: 14px;
}

#coasterHud span:last-child {
  transform: translateX(-25px);
}

#coasterOverlay {
  position: absolute;
  inset: 0;
  z-index: 10;
  background: rgba(80, 34, 63, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: #5c2340;
}

#coasterOverlay.hidden {
  display: none;
}

.coasterCard {
  width: 500px;
  padding: 28px;
  border-radius: 18px;
  background: #fff7bd;
  border: 4px solid #ff9abb;
  box-shadow: 0 8px 0 #d86f96;
}

.coasterCard h1 {
  margin: 0 0 10px;
  color: #d84d7c;
  font-size: 38px;
}

.coasterCard p {
  font-size: 16px;
  line-height: 1.5;
}

.coasterCard button {
  border: 0;
  background: #ff6f9f;
  color: white;
  padding: 11px 24px;
  border-radius: 999px;
  font-weight: bold;
  cursor: pointer;
  font-size: 15px;
  box-shadow: 0 5px 0 #bd3e6c;
}

.coasterCard button:hover {
  transform: translateY(2px);
  box-shadow: 0 3px 0 #bd3e6c;
}

JavaScript

(function () {
  var canvas = document.getElementById("coasterCanvas");
  var ctx = canvas.getContext("2d");
  var overlay = document.getElementById("coasterOverlay");
  var startBtn = document.getElementById("coasterStart");
  var ticketText = document.getElementById("ticketScore");
  var lifeText = document.getElementById("lifeScore");
  var root = document.getElementById("coasterGame");

  var W = canvas.width;
  var H = canvas.height;

  /*
    INSERT YOUR OWN IMAGE URLS HERE.
    Cart = is the   
    Ferris wheel = Background decoration
    Tent = Background decoration
    Barrel = Obstacle on the tracks, used to be barrels but switched with clowns to fit theme
    Ticket = VP themed? Also collectables*/

  var IMAGE_URLS = {
    cart: "https://i.postimg.cc/0QwN2H8c/Circus-(1).png",
    ferrisWheel: "https://i.postimg.cc/1XnLwLgq/Circus.gif",
    tent: "https://i.postimg.cc/DyzWk699/Circus.png",
    barrel: "https://i.postimg.cc/s2bxpRx9/httpsi-postimg-cc-Ssz-X5x-Hyimage-png.png",
    ticket: "https://i.postimg.cc/4y7mcS4P/Design-uden-navn-(49).png"
  };

  /*
    IMAGE SIZE SETTINGS

    You can adjust these depending on how you want your game to look.  */
  var IMAGE_SETTINGS = {
    cart: {
      width: 105,
      height: 80,
      offsetX: -52,
      offsetY: -42
    },

    ferrisWheel: {
      width: 230,
      height: 230,
      offsetX: -115,
      offsetY: -115
    },

    tent: {
      width: 175,
      height: 145,
      offsetY: -145
    },

    barrel: {
      width: 84,
      height: 84,
      offsetX: -25,
      offsetY: -84
    },

    ticket: {
      width: 42,
      height: 30,
      offsetX: -21,
      offsetY: -15
    }
  };

  var images = {};

  function makeImage(name, url) {
    images[name] = {
      img: new Image(),
      loaded: false,
      failed: false
    };

    if (!url) {
      images[name].failed = true;
      return;
    }

   ...