12 September 2025

An easy-to-follow guide on making an interactive Magic Ball game (code samples included)

Anton Diduh Content writer & Video content creator at Stripo
Rolenko Liubov Web Designer/HTML Coder at Stripo

Summarize

ChatGPT Perplexity

In this article, we’ll show you how to create an interactive Magic Ball game. We’ll walk you through the whole process, give you the necessary code pieces, and explain to you how everything works, so you can create your own interactive game.

Everyone wants to know what awaits them in the future or whether they will be lucky. Remember those funny balls that, if you shake them, give answers to your questions? Now we can safely say that these balls are not only in toy stores, but also in your emails, because we are happy to introduce you to the new interactive Magic Ball game. We don’t know about your future, but the one thing we surely know without a magic ball is that you are destined to create this email game with ease, thanks to our guide.

This game mechanic looks pretty straightforward, but you can come up with many uses for your marketing campaigns, like:

  • grant random discount or promo codes for the recipients to use later;
  • make seasonal promotions for your goods where each answer promotes a different brand or item;
  • raise overall engagement and bring a fun aspect to your emails, by making regular newsletters with future predictions;
  • and more.

In this article, we will show you in detail how to create a full-fledged game that will include the following:

  • AMP version of the game;
  • kinetic, built with HTML5 and CSS3, version;
  • fallback version for email clients that do not support interactivity.

So, let’s dive right in.

How this game works

The game is rather simple. It has only two active buttons. Once you click on the Start button, the Magic Ball shows a random answer, just like the real magic ball toy. If you want to see other answers and ask something again, click on the Try again button, and the ball will show you a different random answer.

AMP version

The first thing we need to do is prepare a platform for our future game. Insert an empty 1-column structure into the template, select it, and specify it to be included only in AMP HTML.

After that, add the HTML block to it.

Once it’s done, it’s time to add the following code:

<style amp-custom>
    .magic-ball-amp {
        text-align: center;
    }
 
    .magic-ball-amp .ball-container {
        width: 280px;
        height: 280px;
        position: relative;
        margin: 0 auto 15px;
        box-shadow: 0 4px 15px 0 rgba(91, 91, 109, .5);
        border-radius: 50%;
        overflow: hidden;
    }
 
    .magic-ball-amp .ball {
        display: flex;
        width: 100%;
        height: 100%;
        position: relative;
        justify-content: center;
        background-color: #010101;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/bg.png);
        background-repeat: no-repeat;
        background-position: 0 0;
        background-size: cover;
    }
 
    .magic-ball-amp .front {
        width: 50%;
        height: 50%;
        position: absolute;
        margin: 25%;
        border-radius: 50%;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/front.png);
        background-size: contain;
        position: absolute;
    }
 
    .magic-ball-amp .back {
        position: absolute;
        transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85);
        width: 60%;
        height: 60%;
        position: absolute;
        margin: 20%;
        background-repeat: no-repeat;
        background-position: center center;
        background-size: cover;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/back2.png);
    }
 
    .magic-ball-amp .back div {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
 
    .magic-ball-amp .back h4 {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 90%;
        margin: 0 auto;
        color: #fff;
        font-size: 18px;
        text-align: center;
    }
 
    .magic-ball-amp .btn {
        display: inline-block;
        background-color: #fff;
        border: 2px solid #329CA0;
        border-radius: 16px;
        color: #329CA0;
        cursor: pointer;
        font-size: 18px;
        line-height: 28px;
        padding: 4px 32px;
    }
 
    .magic-ball-amp .btn:hover {
        color: #fff;
        background-color: #329CA0;
    }
 
    .magic-ball-amp .visible-img {
        visibility: visible;
        transition-delay: 0.3s;
    }
 
    .magic-ball-amp .hidden-img {
        visibility: hidden;
    }
 
    .magic-ball-amp .reveal .front {
        transition: transform 0.6s;
        transform: translateX(-265px) translateY(-15px) skewX(-15deg) skewY(0) scale(0.4, 0.9);
    }
 
    .magic-ball-amp .reveal .back {
        transition: transform 0.6s;
        transform: translateX(0) translateY(0) skewX(0) skewY(0) scale(1);
    }
 
    @media only screen and (max-width: 600px) {
        .magic-ball-amp .back h4 {
            font-size: 16px;
        }
    }
</style>
<div class="magic-ball-amp">
  <div class="ball-container">
    <div class="ball">
      <span class="front"></span>
      <div class="back">
        <div>
          <h4>
            It is certain
          </h4>
        </div>
        <div>
          <h4>
            It is decidedly so
          </h4>
        </div>
        <div>
          <h4>
            Ask again later
          </h4>
        </div>
        <div>
          <h4>
            Better not tell you now
          </h4>
        </div>
        <div>
          <h4>
            My reply is no
          </h4>
        </div>
        <div>
          <h4>
            My sources say no
          </h4>
        </div>
        <div>
          <h4>
            Yes definitely
          </h4>
        </div>
        <div>
          <h4>
            As I see it, yes
          </h4>
        </div>
      </div>
    </div>
  </div>
  <p>
    <button class="btn">
      Start
    </button>
  </p>
  <p>
    <button class="btn">
      Try again
    </button>
  </p>
</div>

Now, your template should look like this. It’s a basic layout of the game, without any interactivity (just yet).

At its core, the game consists of three pictures:

  1. Image of a black ball.
  2. Image with the number 8.
  3. Background for the message.

Now, it’s time to add the first bits of interactive action. And we start with our buttons.

Add this code to the “Start” button:


on="tap:AMP.setState({reveal: true})"

And this one to the “Try again” button:


on="tap:AMP.setState({reveal: false})"

You need to change these lines with the code above:

And once it’s done, it must look like this.

Each code piece has a on="tap:" part, which is the “click” event handler and creates variables when the button is clicked. Meanwhile, AMP.setState({ })" is a special syntax for creating variables, and reveal: is a variable for determining whether the ball is flipped or not.

After that, we need to set the condition for displaying the block with the class ball-container using the following code:


<div [class]="reveal == true ? 'ball-container reveal' : 'ball-container'" class="ball-container"> 

To make everything right, transform this code line, which is already there:

Into this one with the code we gave you above:

Don’t be scared of the &#39 symbols that you can see on our screenshot. They appear as our code editor replaces quotation marks and other symbols with HTML code, where &#39 is a single quote. You will have them too, and you will see them in our final code piece, so everything is alright.

Basically, if the value of the reveal variable is true, the ball-container and reveal classes are added; otherwise, only the ball-container class is added.

The style code you added at the very beginning already has the reveal class described:

The front is an image with the number eight, and the back is the reverse side with a background for the message. Using the transform property, we simulate rotation, and transition sets the time in seconds.

The back class also has a default transform style, which hides the back side at the start of the game.

You know what else we should hide? Our buttons when showing “predictions.” To make this right, we add the attribute [hidden]="reveal" to the <p> tag containing the “Start” button. When the value of the reveal variable is true, the attribute hidden will be added, and the button will be hidden. And the attributes hidden [hidden]="!reveal" will be added to the <p> tag containing the “Try again” button.

The whole code looks like this:


<p [hidden]="reveal" ><button on="tap:AMP.setState({reveal: true})" class="btn"> Start </button></p>
<p hidden [hidden]="!reveal"><button on="tap:AMP.setState({reveal: false})" class="btn"> Try again </button></p>

And once you’ve done, it must look like this:

After all manipulations, the ball now rotates, and the buttons are hidden. All that remains is to implement the display of one random message, as in a real magic ball. To make it happen, we need to change our “Start” button code to this one:


on="tap:AMP.setState({reveal: true, num: floor(random() * floor(8)+1)})"

So it will look like this:

In the entry num: floor(random() * floor(8)+1), we wrote a random value from 1 to 8 into the variable num (as our game includes 8 different answers).

Next, we specify display conditions for all responses. We need to display messages with a delay, so we will use the visibility property. We already have 2 classes described for this in the code you pasted at the very start:

Now, these classes need to be added to the answer code. All div tags that contain predictions need to have the following code added, changing only the number. In the entry num == 1 it should be 1,2,3,4 in ascending order:

[class]="num == 1 ? 'visible-img' : 'hidden-img'" class="hidden-img"

Depending on the value of num, we add the class visible-img or hidden-img, and also hide all messages by default by adding class="hidden-img".

Here’s the whole code sample for this:

<div [class]="num == 1 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            It is certain
          </h4>
        </div>
        <div [class]="num == 2 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            It is decidedly so
          </h4>
        </div>
        <div [class]="num == 3 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            Ask again later
          </h4>
        </div>
        <div [class]="num == 4 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            Better not tell you now
          </h4>
        </div>
        <div [class]="num == 5 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            My reply is no
          </h4>
        </div>
        <div [class]="num == 6 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            My sources say no
          </h4>
        </div>
        <div [class]="num == 7 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            Yes definitely
          </h4>
        </div>
        <div [class]="num == 8 ? 'visible-img' : 'hidden-img'" class="hidden-img">
          <h4>
            As I see it, yes
          </h4>
        </div>

In the end, it all must look like this:

And it’s done. To make sure that you did everything right, we leave both final code samples, so you can check them with your own creation

Basic code with styles:

<style amp-custom>
    .magic-ball-amp {
        text-align: center;
    }
 
    .magic-ball-amp .ball-container {
        width: 280px;
        height: 280px;
        position: relative;
        margin: 0 auto 15px;
        box-shadow: 0 4px 15px 0 rgba(91, 91, 109, .5);
        border-radius: 50%;
        overflow: hidden;
    }
 
    .magic-ball-amp .ball {
        display: flex;
        width: 100%;
        height: 100%;
        position: relative;
        justify-content: center;
        background-color: #010101;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/bg.png);
        background-repeat: no-repeat;
        background-position: 0 0;
        background-size: cover;
    }
 
    .magic-ball-amp .front {
        width: 50%;
        height: 50%;
        position: absolute;
        margin: 25%;
        border-radius: 50%;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/front.png);
        background-size: contain;
        position: absolute;
    }
 
    .magic-ball-amp .back {
        position: absolute;
        transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85);
        width: 60%;
        height: 60%;
        position: absolute;
        margin: 20%;
        background-repeat: no-repeat;
        background-position: center center;
        background-size: cover;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/back2.png);
    }
 
    .magic-ball-amp .back div {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
 
    .magic-ball-amp .back h4 {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 90%;
        margin: 0 auto;
        color: #fff;
        font-size: 18px;
        text-align: center;
    }
 
    .magic-ball-amp .btn {
        display: inline-block;
        background-color: #fff;
        border: 2px solid #329CA0;
        border-radius: 16px;
        color: #329CA0;
        cursor: pointer;
        font-size: 18px;
        line-height: 28px;
        padding: 4px 32px;
    }
 
    .magic-ball-amp .btn:hover {
        color: #fff;
        background-color: #329CA0;
    }
 
    .magic-ball-amp .visible-img {
        visibility: visible;
        transition-delay: 0.3s;
    }
 
    .magic-ball-amp .hidden-img {
        visibility: hidden;
    }
 
    .magic-ball-amp .reveal .front {
        transition: transform 0.6s;
        transform: translateX(-265px) translateY(-15px) skewX(-15deg) skewY(0) scale(0.4, 0.9);
    }
 
    .magic-ball-amp .reveal .back {
        transition: transform 0.6s;
        transform: translateX(0) translateY(0) skewX(0) skewY(0) scale(1);
    }
 
    @media only screen and (max-width: 600px) {
        .magic-ball-amp .back h4 {
            font-size: 16px;
        }
    }
</style>
<div class="magic-ball-amp">
  <div class="ball-container">
    <div class="ball">
      <span class="front"></span>
      <div class="back">
        <div>
          <h4>
            It is certain
          </h4>
        </div>
        <div>
          <h4>
            It is decidedly so
          </h4>
        </div>
        <div>
          <h4>
            Ask again later
          </h4>
        </div>
        <div>
          <h4>
            Better not tell you now
          </h4>
        </div>
        <div>
          <h4>
            My reply is no
          </h4>
        </div>
        <div>
          <h4>
            My sources say no
          </h4>
        </div>
        <div>
          <h4>
            Yes definitely
          </h4>
        </div>
        <div>
          <h4>
            As I see it, yes
          </h4>
        </div>
      </div>
    </div>
  </div>
  <p>
    <button class="btn">
      Start
    </button>
  </p>
  <p>
    <button class="btn">
      Try again
    </button>
  </p>
</div>

Interactive game code:

<div esd-text="true" class="magic-ball-amp esd-text">
  <div [class]="reveal == true ? &#39;ball-container reveal&#39; : &#39;ball-container&#39;" class="ball-container">
    <div class="ball">
      <span class="front"></span>
      <div class="back">
        <div [class]="num == 1 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            It is certain
          </h4>
        </div>
        <div [class]="num == 2 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            It is decidedly so
          </h4>
        </div>
        <div [class]="num == 3 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            Ask again later
          </h4>
        </div>
        <div [class]="num == 4 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            Better not tell you now
          </h4>
        </div>
        <div [class]="num == 5 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            My reply is no
          </h4>
        </div>
        <div [class]="num == 6 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            My sources say no
          </h4>
        </div>
        <div [class]="num == 7 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            Yes definitely
          </h4>
        </div>
        <div [class]="num == 8 ? &#39;visible-img&#39; : &#39;hidden-img&#39;" class="hidden-img">
          <h4>
            As I see it, yes
          </h4>
        </div>
      </div>
    </div>
  </div>
  <p [hidden]="reveal">
    <button on="tap:AMP.setState({reveal: true, num: floor(random() * floor(8)+1)})" class="btn">
      Start
    </button>
  </p>
  <p [hidden]="!reveal" hidden>
    <button on="tap:AMP.setState({reveal: false})" class="btn">
      Try again
    </button>
  </p>
</div>

Kinetic version built with HTML5 and CSS3

The next step of our guide is the kinetic version, also known as Interactive HTML, built with HTML5 & CSS3. Now, let’s add another empty 1-column structure. Select it, and pick an “Include in HTML only” option to make a foundation for our HTML game version.

After that, we drop in this structure HTML block and paste this code in this block:

<style>
    .magic-ball-html {
        text-align: center;
    }
 
    .magic-ball-html .ball-container {
        width: 280px;
        height: 280px;
        position: relative;
        margin: 0 auto 15px;
        box-shadow: 0 4px 15px 0 rgba(91, 91, 109, .5);
        border-radius: 50%;
        overflow: hidden;
    }
 
    .magic-ball-html .ball {
        display: flex;
        width: 100%;
        height: 100%;
        position: relative;
        justify-content: center;
        background-color: #010101;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/bg.png);
        background-repeat: no-repeat;
        background-position: 0 0;
        background-size: cover;
    }
 
    .magic-ball-html .front {
        width: 50%;
        height: 50%;
        position: absolute;
        margin: 25%;
        border-radius: 50%;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/front.png);
        background-size: contain;
        position: absolute;
    }
 
    .magic-ball-html .back {
        position: absolute;
        transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85);
        width: 60%;
        height: 60%;
        position: absolute;
        margin: 20%;
        background-repeat: no-repeat;
        background-position: center center;
        background-size: cover;
        background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/back2.png);
    }
 
    .magic-ball-html .back div {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        visibility: hidden;
    }
 
    .magic-ball-html .back h4 {
        display: flex;
        justify-content: center;
        align-items: center;
        margin: 0 auto !important;
        width: 90%;
        color: #fff;
        font-size: 18px;
        text-align: center;
    }
 
    @media only screen and (max-width: 600px) {
        .magic-ball-html .back h4 {
            font-size: 16px !important;
        }
    }
 
    .magic-ball-html .btn {
        display: inline-block;
        background-color: #fff;
        border: 2px solid #329CA0;
        border-radius: 16px;
        color: #329CA0;
        cursor: pointer;
        font-size: 18px;
        line-height: 28px;
        padding: 4px 32px;
    }
 
    .magic-ball-html .btn:hover {
        color: #fff;
        background-color: #329CA0;
    }
 
    .magic-ball-html .start,
    .magic-ball-html .again {
        display: none;
    }
 
    .input-start:checked~* .front {
        transition: transform 0.6s;
        transform: translateX(-265px) translateY(-15px) skewX(-15deg) skewY(0) scale(0.4, 0.9) !important;
    }
 
    .input-start:checked~* .back {
        transition: transform 0.6s;
        transform: translateX(0) translateY(0) skewX(0) skewY(0) scale(1) !important;
    }
 
    .input-again:checked~* .front {
        transform: none;
    }
 
    .input-again:checked~* .back {
        transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85);
    }
 
    #input-start-1:checked~* .result-1,
    #input-start-2:checked~* .result-2,
    #input-start-3:checked~* .result-3,
    #input-start-4:checked~* .result-4,
    #input-start-5:checked~* .result-5,
    #input-start-6:checked~* .result-6,
    #input-start-7:checked~* .result-7,
    #input-start-8:checked~* .result-8 {
        visibility: visible;
        transition-delay: 0.3s;
    }
 
    #input-again-1:checked~* .result-1,
    #input-again-2:checked~* .result-2,
    #input-again-3:checked~* .result-3,
    #input-again-4:checked~* .result-4,
    #input-again-5:checked~* .result-5,
    #input-again-6:checked~* .result-6,
    #input-again-7:checked~* .result-7,
    #input-again-8:checked~* .result-8 {
        visibility: hidden !important;
        transition-delay: 0.3s;
    }
 
    #input-start-1:checked~* .start-1,
    #input-start-2:checked~* .start-2,
    #input-start-3:checked~* .start-3,
    #input-start-4:checked~* .start-4,
    #input-start-5:checked~* .start-5,
    #input-start-6:checked~* .start-6,
    #input-start-7:checked~* .start-7,
    #input-start-8:checked~* .start-8 {
        display: none !important;
    }
 
    #input-again-1:checked~* .again-1,
    #input-again-2:checked~* .again-2,
    #input-again-3:checked~* .again-3,
    #input-again-4:checked~* .again-4,
    #input-again-5:checked~* .again-5,
    #input-again-6:checked~* .again-6,
    #input-again-7:checked~* .again-7,
    #input-again-8:checked~* .again-8 {
        display: none !important;
    }
 
    #input-start-1:checked~* .again-1,
    #input-start-2:checked~* .again-2,
    #input-start-3:checked~* .again-3,
    #input-start-4:checked~* .again-4,
    #input-start-5:checked~* .again-5,
    #input-start-6:checked~* .again-6,
    #input-start-7:checked~* .again-7,
    #input-start-8:checked~* .again-8 {
        display: block;
    }
 
    #input-again-1:checked~* .start-2,
    #input-again-2:checked~* .start-3,
    #input-again-3:checked~* .start-4,
    #input-again-4:checked~* .start-5,
    #input-again-5:checked~* .start-6,
    #input-again-6:checked~* .start-7,
    #input-again-7:checked~* .start-8 {
        display: block;
    }
 
    #input-again-1:checked~* .start-1,
    #input-again-2:checked~* .start-2,
    #input-again-3:checked~* .start-3,
    #input-again-4:checked~* .start-4,
    #input-again-5:checked~* .start-5,
    #input-again-6:checked~* .start-6,
    #input-again-7:checked~* .start-7 {
        display: none;
    }
</style>
  <div class="magic-ball-html">
    <form>
      <input id="input-start-1" type="radio" name="item-1" class="input-start" style="display:none">
      <input type="radio" name="item-1" id="input-again-1" class="input-again" style="display:none">
      <input type="radio" name="item-2" id="input-start-2" class="input-start" style="display:none">
      <input id="input-again-2" type="radio" name="item-2" class="input-again" style="display:none">
      <input id="input-start-3" type="radio" name="item-3" class="input-start" style="display:none">
      <input name="item-3" id="input-again-3" type="radio" class="input-again" style="display:none">
      <input id="input-start-4" type="radio" name="item-4" class="input-start" style="display:none">
      <input id="input-again-4" type="radio" name="item-4" class="input-again" style="display:none">
      <input id="input-start-5" type="radio" name="item-5" class="input-start" style="display:none">
      <input id="input-again-5" type="radio" name="item-5" class="input-again" style="display:none">
      <input name="item-6" id="input-start-6" type="radio" class="input-start" style="display:none">
      <input id="input-again-6" type="radio" name="item-6" class="input-again" style="display:none">
      <input name="item-7" id="input-start-7" type="radio" class="input-start" style="display:none">
      <input id="input-again-7" type="radio" name="item-7" class="input-again" style="display:none">
      <input id="input-start-8" type="radio" name="item-8" class="input-start" style="display:none">
      <div class="ball-container">
        <div class="ball">
          <span class="front"></span>
          <div class="back">
            <div class="result-1">
              <h4 style="font-size:18px;color:#ffffff;padding-bottom:5px">
                It is certain
              </h4>
            </div>
            <div class="result-2">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                My reply is no
              </h4>
            </div>
            <div class="result-3">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                It is decidedly so
              </h4>
            </div>
            <div class="result-4">
              <h4 style="padding-bottom:5px;color:#ffffff;font-size:18px">
                Ask again later
              </h4>
            </div>
            <div class="result-5">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                Yes definitely
              </h4>
            </div>
            <div class="result-6">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                My sources say no
              </h4>
            </div>
            <div class="result-7">
              <h4 style="color:#ffffff;font-size:18px;padding-bottom:5px">
                Better not tell you now
              </h4>
            </div>
            <div class="result-8">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                As I see it, yes
              </h4>
            </div>
          </div>
        </div>
      </div>
      <div>
        <p class="start-1">
          <label for="input-start-1" class="btn">
            Start
          </label>
        </p>
        <p class="again-1 again">
          <label for="input-again-1" class="btn">
            Try again
          </label>
        </p>
        <p class="start-2 start">
          <label for="input-start-2" class="btn">
            Start
          </label>
        </p>
        <p class="again-2 again">
          <label for="input-again-2" class="btn">
            Try again
          </label>
        </p>
        <p class="start-3 start">
          <label for="input-start-3" class="btn">
            Start
          </label>
        </p>
        <p class="again-3 again">
          <label for="input-again-3" class="btn">
            Try again
          </label>
        </p>
        <p class="start-4 start">
          <label for="input-start-4" class="btn">
            Start
          </label>
        </p>
        <p class="again-4 again">
          <label for="input-again-4" class="btn">
            Try again
          </label>
        </p>
        <p class="start-5 start">
          <label for="input-start-5" class="btn">
            Start
          </label>
        </p>
        <p class="again-5 again">
          <label for="input-again-5" class="btn">
            Try again
          </label>
        </p>
        <p class="start-6 start">
          <label for="input-start-6" class="btn">
            Start
          </label>
        </p>
        <p class="again-6 again">
          <label for="input-again-6" class="btn">
            Try again
          </label>
        </p>
        <p class="start-7 start">
          <label for="input-start-7" class="btn">
            Start
          </label>
        </p>
        <p class="again-7 again">
          <label for="input-again-7" class="btn">
            Try again
          </label>
        </p>
        <p class="start-8 start">
          <label for="input-start-8" class="btn">
            Start
          </label>
        </p>
        <p class="again-8 again">
          <button type="reset" class="btn">
            Try again
          </button>
        </p>
      </div>
    </form>
  </div>

The kinetic version has a similar structure, but all the interactivity is done using the input and label tags. However, there is a nuance here. We cannot use random number generation, so further in the code, we added buttons that will show different messages, creating a sense of randomness.

Instead of button tags, label tags are inserted and bound to specific inputs using the for=" attribute. All buttons except the first and last are the same, with only numbers differing.

There are a few interesting details in this code that are worth noting. The first button does not have a start class, but only start-1. Meanwhile, the last button has a different code — button type="reset". We wrapped the entire code in a <form> tag so that when all the messages are passed, the button type="reset" will clear the form settings (marked inputs) and the game will start over. This will be unnoticeable, since the message sequence will simply be shown all over again.

The inputs for the buttons are also the same, and they are added at the very beginning. It is important that all input type=”radio” and each pair of buttons “Start” and “Try again“ have the same name attribute. This is done so that the animation styles are removed and added again; otherwise, the animation will not work.

Styles part

We decided to highlight styles in a separate section, since there are also nuances here, thanks to which the game works. All styles for the kinetic version are at the beginning of the code in the <style></style> part here:

And here:

For example, the animation of the ball rotation is made using the :checked: property.

Also, all buttons except the first one are hidden using the start and again classes.

To display the buttons, certain styles are used, each for its own stage of the game.

First, when we press the button, it needs to be hidden. The styles are as follows:

The following styles are needed if Start 1 is pressed and it is necessary to show Try again 1, if Try again 1 is pressed, it is necessary to show Start 2, and so on.

The last styles show and hide the corresponding prediction.

Fallback version

For email clients that don’t support HTML5 and CSS3, you need to create an additional block with code. It will have a design similar to our mechanics but without interactivity. Clicking on the elements will lead to the web version of the email. The fallback version for this game will include an image of a ball and a “Start” button.

We continue to work on the block with interactive HTML, which we made above. Paste the following code between the </style> and <div class="magic-ball-html">.

 
<input type="checkbox" id="fallback_ctrl" checked class="fallback_ctrl" style="display:none !important;mso-hide:all">
<!-- FALLBACK -->
<span id="fallback" class="fallback">
  <table cellpadding="0" cellspacing="0" width="100%">
    <tbody>
      <tr>
        <td align="center" style="padding-bottom:20px">
          <a target="_blank" href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml"><img alt="" width="280" height="280" src="https://zlnfb.stripocdn.email/content/guids/CABINET_ae104e7a5ceaf664ad6a35bde5d1cf9141bcfd7f1155283b7f48a4f4992820c2/images/bgfallbac.png"></a>
        </td>
      </tr>
      <tr>
        <td align="center" class="esd-block-button">
          <!--[if mso]><a href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" target="_blank" hidden>
	<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" esdevVmlButton href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" style="height:NaNpx; v-text-anchor:middle; width:NaNpx" arcsize="50%" strokecolor="#329ca0" strokeweight="1px" fillcolor="#ffffff">
		<w:anchorlock></w:anchorlock>
		<center style='color:#329ca0; font-family:arial, "helvetica neue", helvetica, sans-serif; font-size:NaNpx; font-weight:400; line-height:NaNpx;  mso-text-raise:1px'>Start</center>
	</v:roundrect></a>
<![endif]-->
          <!--[if !mso]><!-- -->
          <span class="es-button-border"><a href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" target="_blank" class="es-button">Start</a></span>
          <!--<![endif]-->
        </td>
      </tr>
    </tbody>
  </table>
</span>
<!-- /FALLBACK -->
<!--[if !mso]><!-- -->
<!-- INTERACTIVE ELEMENT -->
<div class="container" style="display:none;mso-hide:all">

After that, paste this code at the very end of the email, so it will look like this.

</div><!-- /INTERACTIVE ELEMENT -->
<!--<![endif]-->

Besides that, you can change the links, replacing them with your own here:

To change the image of your magic ball, you can replace this link:

The button here has a special code for Outlook. Therefore, if you want to change its style, it is best to create a separate button using the “Button” block and customize it as you need. Then open the code and copy the entire code.

After that, open the HTML version of the game and replace the part with the button from <td> to </td> with your own code.

Styles part

As in the interactive version guide, we make a separate section about styles. For the fallback version, styles are needed to show only the version suitable for the email client and make the buttons look the same as in the interactive version.

Paste this code within the <style></style> tag in your HTML version of the game:

    /* --- */
    @media screen and (-webkit-min-device-pixel-ratio: 0) {
        input.fallback_ctrl:checked~.container {
            display: block !important;
        }
 
        input.fallback_ctrl:checked~#fallback {
            display: none !important;
        }
    }
 
    [owa] .container {
        display: none !important;
    }
 
    [class~="x_container"] {
        display: none !important;
    }
 
    [id~="x_fallback"] {
        display: block !important;
    }
 
    @media screen and (max-width: 600px) {
        body[data-outlook-cycle] #fallback {
            display: block !important;
        }
 
        body[data-outlook-cycle] .container {
            display: none !important;
        }
    }

Now, let’s dive into the details of how these styles work. For example, this part:

<!--[if !mso]><!--><input type="checkbox" id="fallback_ctrl" class="fallback_ctrl" style="display:none !important;mso-hide:all;" checked>

<!--<![endif]-->

It’s needed to show or hide the fallback via styles. We enclosed in comments <!--[if !mso]><!--> … <!--<![endif]--> to ensure it is hidden in the Outlook Desktop client.

Meanwhile, <div id="fallback" class="fallback"></div> is a block containing the entire layout of our fallback. It should have a simple, tabular layout suitable for Outlook. In our example, this is a table with links that lead to the web version. You can create your own version. The main thing is to use a layout that is understandable for Outlook.

The styles below hide and show the fallback version. If you remove them or comment them out, the fallback version will be visible, and you can adjust its design to the desired form. But don't forget to return these styles before sending the email.

These styles don’t have clear rules for each email client, but there is a set of hacks that can be used to control the display:

  • styles that start with [owa] are used for Outlook;
  • the [class~="x_container"] styles are needed for Outlook in case [owa] does not work;
  • body[data-outlook-cycle] styles needed for Outlook on iOS and Android mobile devices;
  • mso-hide:all; is used for Outlook.com.

The full code

Here is the fallback code of the game, including the interactive HTML part and the fallback version:

<style>
  .magic-ball-html { text-align: center; } .magic-ball-html .ball-container { width: 280px; height: 280px; position: relative; margin: 0 auto 15px; box-shadow: 0 4px 15px 0 rgba(91, 91, 109, .5); border-radius: 50%; overflow: hidden; } .magic-ball-html .ball { display: flex; width: 100%; height: 100%; position: relative; justify-content: center; background-color: #010101; background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/bg.png); background-repeat: no-repeat; background-position: 0 0; background-size: cover; } .magic-ball-html .front { width: 50%; height: 50%; position: absolute; margin: 25%; border-radius: 50%; background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/front.png); background-size: contain; position: absolute; } .magic-ball-html .back { position: absolute; transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85); width: 60%; height: 60%; position: absolute; margin: 20%; background-repeat: no-repeat; background-position: center center; background-size: cover; background-image: url(https://zlnfb.stripocdn.email/content/guids/CABINET_bbdda5ca4226da4c50115c0b4dba63360c0e1d408c582919c464939f5ce3a420/images/back2.png); } .magic-ball-html .back div { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); visibility: hidden; } .magic-ball-html .back h4 { display: flex; justify-content: center; align-items: center; margin: 0 auto !important; width: 90%; color: #fff; font-size: 18px; text-align: center; } @media only screen and (max-width: 600px) { .magic-ball-html .back h4 { font-size: 16px !important; } } .magic-ball-html .btn { display: inline-block; background-color: #fff; border: 2px solid #329CA0; border-radius: 16px; color: #329CA0; cursor: pointer; font-size: 18px; line-height: 28px; padding: 4px 32px; } .magic-ball-html .btn:hover { color: #fff; background-color: #329CA0; } .magic-ball-html .start, .magic-ball-html .again { display: none; } .input-start:checked~* .front { transition: transform 0.6s; transform: translateX(-265px) translateY(-15px) skewX(-15deg) skewY(0) scale(0.4, 0.9) !important; } .input-start:checked~* .back { transition: transform 0.6s; transform: translateX(0) translateY(0) skewX(0) skewY(0) scale(1) !important; } .input-again:checked~* .front { transform: none; } .input-again:checked~* .back { transform: translateX(265px) translateY(0) skewX(15deg) skewY(-10deg) scale(0.85); } #input-start-1:checked~* .result-1, #input-start-2:checked~* .result-2, #input-start-3:checked~* .result-3, #input-start-4:checked~* .result-4, #input-start-5:checked~* .result-5, #input-start-6:checked~* .result-6, #input-start-7:checked~* .result-7, #input-start-8:checked~* .result-8 { visibility: visible; transition-delay: 0.3s; } #input-again-1:checked~* .result-1, #input-again-2:checked~* .result-2, #input-again-3:checked~* .result-3, #input-again-4:checked~* .result-4, #input-again-5:checked~* .result-5, #input-again-6:checked~* .result-6, #input-again-7:checked~* .result-7, #input-again-8:checked~* .result-8 { visibility: hidden !important; transition-delay: 0.3s; } #input-start-1:checked~* .start-1, #input-start-2:checked~* .start-2, #input-start-3:checked~* .start-3, #input-start-4:checked~* .start-4, #input-start-5:checked~* .start-5, #input-start-6:checked~* .start-6, #input-start-7:checked~* .start-7, #input-start-8:checked~* .start-8 { display: none !important; } #input-again-1:checked~* .again-1, #input-again-2:checked~* .again-2, #input-again-3:checked~* .again-3, #input-again-4:checked~* .again-4, #input-again-5:checked~* .again-5, #input-again-6:checked~* .again-6, #input-again-7:checked~* .again-7, #input-again-8:checked~* .again-8 { display: none !important; } #input-start-1:checked~* .again-1, #input-start-2:checked~* .again-2, #input-start-3:checked~* .again-3, #input-start-4:checked~* .again-4, #input-start-5:checked~* .again-5, #input-start-6:checked~* .again-6, #input-start-7:checked~* .again-7, #input-start-8:checked~* .again-8 { display: block; } #input-again-0:checked~* .start-1, #input-again-1:checked~* .start-2, #input-again-2:checked~* .start-3, #input-again-3:checked~* .start-4, #input-again-4:checked~* .start-5, #input-again-5:checked~* .start-6, #input-again-6:checked~* .start-7, #input-again-7:checked~* .start-8 { display: block; } #input-again-1:checked~* .start-1, #input-again-2:checked~* .start-2, #input-again-3:checked~* .start-3, #input-again-4:checked~* .start-4, #input-again-5:checked~* .start-5, #input-again-6:checked~* .start-6, #input-again-7:checked~* .start-7 { display: none; } /* --- */ @media screen and (-webkit-min-device-pixel-ratio: 0) { input.fallback_ctrl:checked~.container { display: block !important; } input.fallback_ctrl:checked~#fallback { display: none !important; } } [owa] .container { display: none !important; } [class~="x_container"] { display: none !important; } [id~="x_fallback"] { display: block !important; } @media screen and (max-width: 600px) { body[data-outlook-cycle] #fallback { display: block !important; } body[data-outlook-cycle] .container { display: none !important; } }
</style>
<input type="checkbox" id="fallback_ctrl" checked class="fallback_ctrl" style="display:none !important;mso-hide:all">
<!-- FALLBACK -->
<span id="fallback" class="fallback">
  <table cellpadding="0" cellspacing="0" width="100%">
    <tbody>
      <tr>
        <td align="center" style="padding-bottom:20px">
          <a target="_blank" href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml"><img alt="" width="280" height="280" src="https://zlnfb.stripocdn.email/content/guids/CABINET_ae104e7a5ceaf664ad6a35bde5d1cf9141bcfd7f1155283b7f48a4f4992820c2/images/bgfallbac.png"></a>
        </td>
      </tr>
      <tr>
        <td align="center" class="esd-block-button">
          <!--[if mso]><a href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" target="_blank" hidden>
	<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" esdevVmlButton href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" style="height:NaNpx; v-text-anchor:middle; width:NaNpx" arcsize="50%" strokecolor="#329ca0" strokeweight="1px" fillcolor="#ffffff">
		<w:anchorlock></w:anchorlock>
		<center style='color:#329ca0; font-family:arial, "helvetica neue", helvetica, sans-serif; font-size:NaNpx; font-weight:400; line-height:NaNpx;  mso-text-raise:1px'>Start</center>
	</v:roundrect></a>
<![endif]-->
          <!--[if !mso]><!-- -->
          <span class="es-button-border"><a href="https://viewstripo.email/6f489cdc-9552-4074-9bf3-5def90b8d6c41747382475134?type=amphtml" target="_blank" class="es-button">Start</a></span>
          <!--<![endif]-->
        </td>
      </tr>
    </tbody>
  </table>
</span>
<!-- /FALLBACK -->
<!--[if !mso]><!-- -->
<!-- INTERACTIVE ELEMENT -->
<div class="container" style="display:none;mso-hide:all">
  <div class="magic-ball-html">
    <form>
      <input id="input-start-1" type="radio" name="item-1" class="input-start" style="display:none">
      <input type="radio" name="item-1" id="input-again-1" class="input-again" style="display:none">
      <input type="radio" name="item-2" id="input-start-2" class="input-start" style="display:none">
      <input id="input-again-2" type="radio" name="item-2" class="input-again" style="display:none">
      <input id="input-start-3" type="radio" name="item-3" class="input-start" style="display:none">
      <input name="item-3" id="input-again-3" type="radio" class="input-again" style="display:none">
      <input id="input-start-4" type="radio" name="item-4" class="input-start" style="display:none">
      <input id="input-again-4" type="radio" name="item-4" class="input-again" style="display:none">
      <input id="input-start-5" type="radio" name="item-5" class="input-start" style="display:none">
      <input id="input-again-5" type="radio" name="item-5" class="input-again" style="display:none">
      <input name="item-6" id="input-start-6" type="radio" class="input-start" style="display:none">
      <input id="input-again-6" type="radio" name="item-6" class="input-again" style="display:none">
      <input name="item-7" id="input-start-7" type="radio" class="input-start" style="display:none">
      <input id="input-again-7" type="radio" name="item-7" class="input-again" style="display:none">
      <input id="input-start-8" type="radio" name="item-8" class="input-start" style="display:none">
      <div class="ball-container">
        <div class="ball">
          <span class="front"></span>
          <div class="back">
            <div class="result-1">
              <h4 style="font-size:18px;color:#ffffff;padding-bottom:5px">
                It is certain
              </h4>
            </div>
            <div class="result-2">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                My reply is no
              </h4>
            </div>
            <div class="result-3">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                It is decidedly so
              </h4>
            </div>
            <div class="result-4">
              <h4 style="padding-bottom:5px;color:#ffffff;font-size:18px">
                Ask again later
              </h4>
            </div>
            <div class="result-5">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                Yes definitely
              </h4>
            </div>
            <div class="result-6">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                My sources say no
              </h4>
            </div>
            <div class="result-7">
              <h4 style="color:#ffffff;font-size:18px;padding-bottom:5px">
                Better not tell you now
              </h4>
            </div>
            <div class="result-8">
              <h4 style="font-size:18px;padding-bottom:5px;color:#ffffff">
                As I see it, yes
              </h4>
            </div>
          </div>
        </div>
      </div>
      <div>
        <p class="start-1">
          <label for="input-start-1" class="btn">
            Start
          </label>
        </p>
        <p class="again-1 again">
          <label for="input-again-1" class="btn">
            Try again
          </label>
        </p>
        <p class="start-2 start">
          <label for="input-start-2" class="btn">
            Start
          </label>
        </p>
        <p class="again-2 again">
          <label for="input-again-2" class="btn">
            Try again
          </label>
        </p>
        <p class="start-3 start">
          <label for="input-start-3" class="btn">
            Start
          </label>
        </p>
        <p class="again-3 again">
          <label for="input-again-3" class="btn">
            Try again
          </label>
        </p>
        <p class="start-4 start">
          <label for="input-start-4" class="btn">
            Start
          </label>
        </p>
        <p class="again-4 again">
          <label for="input-again-4" class="btn">
            Try again
          </label>
        </p>
        <p class="start-5 start">
          <label for="input-start-5" class="btn">
            Start
          </label>
        </p>
        <p class="again-5 again">
          <label for="input-again-5" class="btn">
            Try again
          </label>
        </p>
        <p class="start-6 start">
          <label for="input-start-6" class="btn">
            Start
          </label>
        </p>
        <p class="again-6 again">
          <label for="input-again-6" class="btn">
            Try again
          </label>
        </p>
        <p class="start-7 start">
          <label for="input-start-7" class="btn">
            Start
          </label>
        </p>
        <p class="again-7 again">
          <label for="input-again-7" class="btn">
            Try again
          </label>
        </p>
        <p class="start-8 start">
          <label for="input-start-8" class="btn">
            Start
          </label>
        </p>
        <p class="again-8 again">
          <button type="reset" class="btn">
            Try again
          </button>
        </p>
      </div>
    </form>
  </div>
</div>
<!-- /INTERACTIVE ELEMENT -->
<!--<![endif]-->

It may look complicated when all game versions are within one email. But we have a dedicated guide where we explain how this works and what benefits it brings.

You might also like

Smart fallbacks for interactive emails: How we handled partial HTML5 supportSmart fallbacks for interactive emails: How we handled partial HTML5 support

Wrapping up

This small game is a treasure trove of possible uses to make your emails engaging and fun. Its randomness feature can bring a sense of surprise to your marketing ideas. Besides that, you can customize the game contents however you like, tweaking it to suit your needs, since now you know how to create it and what’s under the hood of this game.

We hope that this guide will be a helpful support in your journey to make your emails an interactive masterpiece.

Create exceptional emails with Stripo