In this video, we will create a fun web game, in which you need to find the mistakes from two similar images next to each other.

Let’s begin by going to and downloading a royalty-free image for our game.

Then we’ll take the image into an image manipulation program like Photoshop and duplicate it. Now let’s make some changes so that there are at least five mistakes to be found. We got a bit carried away and created eight mistakes.

Save the file and open it in Inkscape. We are using the 1.0 beta version. Make sure you use “open” instead of “import”, otherwise the canvas size will be wrong.

We recommend selecting, “link”, as the import type and “default import resolution” for the Image DPI.

Now let’s draw some hotspots. We’ll give our paths both a fill and a stroke, but drop the opacity of the fill to zero. This is so that the svg-element will recognize clicks on the center of itself, even if we only show the strokes of the elements.

We recommend drawing the hotspots in pairs, because that will make linking them together easier at the coding stage. The hotspots will appear in the code in the same order as they are drawn in Inkscape.

Now let’s speed things up a bit as we draw the rest of the hotspots.

When done, save the file as an optimized SVG. Here’s a quick overview of the settings that worked well for us.

Now we can close Inkscape and take a look at the SVG file in a code editor like VS Code.

The only issue was that the image had a problem with it’s path, but that’s easy to fix by simply deleting this warning and then pointing the image link to the correct location. 

If we now open the file in a browser we can see that we have these awesome responsive vector hotspots.

Now let’s build this into a game.

We’ll start by saving the SVG file as an HTML file. Then we’ll add the default building blocks of an html 5 website. After that we can cut and paste the SVG-element inside the body tags of the web page.

Let’s also add jQuery quickly from a CDN so that we can do some things a bit easier compared to vanilla javascript.

Let’s add a set of script tags and type in our first jQuery aided event handler, in which we check if one of the path elements was clicked, and run a function called fadeOpacity if that was the case.

Now we can define that function and test things out with a simple alert message. As we can see, clicking either on the stroke or on the fill of a path will trigger the alert, which is what we want.

Let’s also trigger a different function if the player clicks somewhere outside of a hotspot. This should introduce some cost to the player so that the game can’t be solved by just quickly clicking on random spots on the page.

This time we’ll target the body element of the page and trigger an alert telling the player to be more careful.

Now clicking outside a hotspot alerts the player about being more careful. However, clicking on a hotspot also gives that alert, even if the first message is about hitting a hotspot. That’s because the click event bubbles up from the path element to also register a click on the body element. To avoid that, we’ll include event dot stopPropagation inside the function that runs when a path is clicked.

Now clicking on a path will not register a click on the body element.

Now let’s create some pairs from the paths by giving them identical class names. 

Next we’ll comment out the alert and add some jQuery powered animation instead. We’ll animate the hotspot from zero opacity to one during two seconds.

Let’s also set the default opacity of the path elements to zero. We’ll do this with a css stylesheet.

Now the hotspots are hidden by default but fade in when clicked.

Next we can create a new variable that stores the number of hotspots the player has already found.

Let’s also create a heads up display with a text element that renders the value of the variable to the page.

While we are creating html elements, let’s also add a text display for keeping track of time.

Let’s set the paragraph elements to act as inline-blocks so that they can live next to each other instead of piling up.

Now we can change the html content of our found id inside the function that triggers when a path has been clicked.

We’ll add the string “mistakes found” and then the value of the foundOnes variable.

Now the text changes every time that a path is clicked. The problem is that each found hotspot should only award us one point. We can fix that by turning the event listener off after the first click.

Now additional clicks no longer change the variable value.

Next we can handle the pairing of the hotspots. Clicking on one hotspot should also reveal it’s pair on the other side.

Let’s create a variable that will store the class name of the clicked element. Then inside the function that handles the click, we’ll use the classList property to get the class.

Now we’ll construct a jQuery class selector by concatenating a dot and the classname. This will then find both elements with that class name and fade them in.

Finally we’ll create a system to keep track of time.

Let’s use setInterval to call a function that we’ll name everySec every one thousand milliseconds which equals one second.

Then we’ll define the everySec function.

Let’s start by checking if the amount of found hotspots is still less than eight, which in our case is the full amount of hotspots. So if all the hotspots have not been found, we should keep increasing the time by one each second.

Let’s create a variable called currentTime for that.

Let’s also render the amount of time on the page by changing the html of the currentTime id.

If the amount of hotspots is not less than eight, it means the player has beat the game and we will write a congratulation instead.

That’s how easy it is to create a find all mistakes game with responsive svg hotspots!

Thanks for watching and see you next time on

Here’s the code:

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <link rel="stylesheet" href="styles.css">


    <div id="hud">

        <p id="foundMistakes">Found mistakes: </p>
        <p id="timePassed">Time passed: </p>


    <svg version="1.1" viewBox="0 0 1920 1080" xmlns="" xmlns:xlink="">
        <image width="1920" height="1080" preserveAspectRatio="none" xlink:href="mistakes.png"/>
        <path class="hotspot1" d="m531 414c126-52.2 45.2 158-15.2 36.8-11-22 4.73-35.2 17.8-48.3" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot1" d="m1501 414c91.4-91.1 79.8 115 12.7 48.3-14.8-14.8-15.4-29.7-12.7-48.3z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot2" d="m527 39.4c74.6-58.8 165 71.2 30.5 71.2-5.61 0-28-14.7-28-20.3 0-20.1-13.6-26.9-2.54-50.8z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot2" d="m1489 49.6c85.2-97.2 88.5 143-6.35 48.3-15.8-15.8-2.74-31.4 3.81-44.5" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot3" d="m104 576c41-125 177 69.9 69.9 69.9-11.1 0-34 4.13-41.9-3.81-21.2-21.2-25.4-26.7-25.4-57.2" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot3" d="m1065 567c144-138 119 158 21.6 61-16-16-23.4-29.3-21.6-54.6" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot4" d="m551 318c142-38.5-10.2 122-10.2 36.8 0-22-1.81-22.2 10.2-36.8z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot4" d="m1512 319c114-83.8 38.8 128-2.54 45.7-8.86-17.7-4.38-29.6 2.54-45.7z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot5" d="m539 211c20.3-39.6 70.1 13.8 43.2 40.7-22.7 22.7-67.7-7.22-44.5-35.6" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot5" d="m1503 199c89.4-6.21 11 109-15.2 30.5-5.11-15.3 4.92-22.7 15.2-30.5z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot6" d="m230 45.7c47-118 104 90.1 3.81 6.35" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot6" d="m1193 33c53.5-96.7 109 79.1 19.1 34.3-18.9-9.45-17.9-16.4-19.1-34.3z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot7" d="m755 845c148-30.3-66.2 95.2 12.7-1.27" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot7" d="m1727 856c120-21.4-55.2 87.1-5.08 1.27" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot8" d="m292 767c82-95.4 65.2 127-5.08 3.81" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>
        <path class="hotspot8" d="m1248 762c99.9-59.1 24 112 0 0z" style="fill-opacity:0;stroke-width:8.2;stroke:#fc0000"/>


           let foundMistakes = 0;
           let wasMistakeJustMade = "no";
           let classNamePair;
           let timePassed = 0;

           setInterval(everySec, 1000);

        function handleClick(){

            classNamePair = this.classList;


            $('.' + classNamePair).animate({opacity: 1}, 1000).off();

            if (wasMistakeJustMade == "no"){

            $("#foundMistakes").html("Found mistakes: " + foundMistakes);
                //alert("You have clicked on " + foundMistakes + " paths");


            } else {

                $("#foundMistakes").html("That's better! Found mistakes: " + foundMistakes);
                //alert("That's better! You have clicked on " + foundMistakes + " paths");


                $(this).animate({opacity: 1}, 1000).off();


                wasMistakeJustMade = "no";



        function punish(){

            alert("Nope, be more careful!");
            wasMistakeJustMade = "yes";


        function everySec(){

            if (foundMistakes < 8){

                $("#timePassed").html("Time passed: " + timePassed);

            } else {

                $("#timePassed").html("Congratulations, you found <span>all</span> the mistakes! Your time was: " + timePassed + " seconds");







path {

    opacity: 0;



No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *