Creating a HTML game with CSS transitions and Javascript

Alvaro Trigo Avatar

Follow on Twitter

I’ve coded a basic interactive game using HTML, CSS transitions and JavaScript and I chose not to use the canvas element for it. I was curious to see how far I could get using basic web technologies. I knew performance would very probably be the critical part, but that made it even more challenging! During this journey I’ve learned a few interesting things on the way that I would like to share here.

Let’s make a HTML game!

One day I though: hey, wouldn’t it be nice to code a simple game with what I know so far? Soon, the idea of a game I used to play when I was young came to my mind.

Typing tutor game by Simon & Schuster.

When I was learning how to type on the computer I used to play an old game built in 1996 called “Typing tutor”. Words were falling from the sky and you had to type them before they reached the ground and destroyed the civilization. It was quite fun! And useful to increase the typing speed!

I though “Yeap! This seems like a simple game I could do for fun!”.

The basics behind the game

The way the game works is pretty straight forward. Words fall from the top with a certain interval and speed. This is done with a simple transition:

var speed = game.g_levels[level].speed * 1000;
var transition = 'all ' + speed + 'ms ' + g_easing;

self.$word.css({
    'transition': transition
});
Code language: JavaScript (javascript)

The transformation is the same for all words. Moving 600 pixels towards the cities.

.word.active {
    transform: translateY(600px);
}
Code language: CSS (css)

Then, the transition speed and the interval change depending on the level. So the higher the level, the faster the transition is and the smallest the interval is. I also added a small difference between the base speed of the words within the same level.

var g_levels = {
    '1': {
        speed: 18,
        fallingLapse: 3000,
        words: 6
    },
    '2': {
        speed: 17,
        fallingLapse: 2800,
        words: 9
    },
    '3': {
        speed: 16,
        fallingLapse: 2600,
        words: 11
    }
};
Code language: JavaScript (javascript)

Missiles work in the same way, but their transition is not dynamic, is always the same.

When a missile hits a word, then a sprite animation of a explosion is displayed in that exact place. How do I detect the collision? By tracking their position on every frame:

function checkCollisions(){
     for(var i = 0; i < g_missiles.length; i++){
        var missile = g_missiles[i];
        var word = missile.word;

        if(missile.$missile[0].getBoundingClientRect().top <= word.$word[0].getBoundingClientRect().top ){
            //destroy word
        }
    }

    requestAnimFrame(checkCollisions);
}
checkCollisions();
Code language: JavaScript (javascript)

I though I could make it even faster by tracking the positions directly in JS by using basics physics equations with linear animations, but I didn’t see any improvement so I kept getBoundingClientRect.

City explosions works exactly in the same way as missile ones, when I detect a collision I show the city explosion sprite and I update the state class of the city, which then shows another image for the damaged city.

State of the cities
.city.state2:before{
    background-image: url(imgs/city-state2.png);
}
.city.state3:before{
    background-image: url(imgs/city-state3.png);
}
Code language: CSS (css)

Dealing with performance

The game went through a few developing phases. The first prototype served as a proof of concept, but it was quite slow in terms of performance to the point of getting stuck in levels with many words on the screen or many animations going on at the same time.

It took a few measures to deal with it:

  • I got rid of jQuery in critical tasks. I would, for instance, replace any $.each loop for a for or any .addClass() for .classList.add. Anything within critical tasks was converted to vanilla Javascript.

  • I deferred elements I wouldn’t need in the DOM. For example, instead of creating all those words or missiles in the DOM from the beginning and having more than a thousand nodes in there, I only load the ones I’ll need for each level. And of course, I won’t add the to the DOM dynamically.

  • I kept the state of the game in Javascript instead of reliying on the DOM for that. This way I would reduce the number of times I access the DOM to read from it and get what I need. I decided to go for an object oriented approach and create objects with all the data I needed for the for the cities, words and missiles.
    I would fill them with data once so I wouldn’t have to access the DOM again. Then, any change would first get reflected on the Javascript object and then on the DOM, so the DOM would only be a reflection of the Javascript state.

So here’s an example of a before and after applying these two techniques:

Originally the code looked like the following:

function onKeyDown(e) {
    var value = String.fromCharCode(e.which);

    $('.word.active').each(function () {
        var currentLetter = $(this).find('span:not(.active)').first();
        var wordText = $(this).data('word');
        if(currentLetter.is(':first-child') ||  currentLetter.prev().hasClass('active')){
            if (currentLetter.text() == value && isWordInProgress(wordText)) {
Code language: JavaScript (javascript)

And after applying the improvements this is how it looks:

function onKeyDown(e) {
    var value = e.key;

    for(var a = 0; a < self.g_firedWords.length; a++){
        var word = self.g_firedWords[a];
        var currentLetter = word.getCurrentLetter();

        if (isWordInProgress(word) && currentLetter === value) {
Code language: JavaScript (javascript)

Notice the amount of times I access the DOM in the first case and how I replaced it all in its second version. Where I store all data in arrays and objects within Javascript.

For example, you can see how in the latest version I kept the fired words in an array instead of having to inspect the DOM to see which ones contain the class active.
Same for each of the active letters in an active words. The object word keeps track of the active letters in the word instead of having to access the DOM to get that information.

Reducing the number of paints

Whean dealing with animations, performance doesn’t just depend on Javascript, but on how we deal with the elements in the screen too.

To get a fast interface we need to simplify paint complexity and reduce paint areas. Thanks to Chrome dev tools we are able to see this in a more visual way when turning on the “Paint flashing” checkbox in Chrome dev tools.

Paint flashing checkbox in chrome dev tools.

I applied these techniques and I was able to save quite a few repaints.

Are you a game designer? Check out some great game design portfolios to get inspired!

Notice how when typing a word, the whole word was being repainting instead of just the letter that changed. Google Chrome shows the the repainted areas in green.

Unncessary repaint resulting in bad performance.

I found out this result was due to the fact that I didn’t use the border-bottom property in the non active letter:

.word span{
    margin: 0 1px 0 0;
}
.word span.active {
    color: #34495e;
    border-bottom: 1px solid #34495e;
}
Code language: CSS (css)

The fix was simply to to let the browser know I was only going to change the color of the border instead of adding one, so I added a transparent border in the non active letter.

.word span{
    margin: 0 1px 0 0;
    border-bottom: 1px solid transparent;
}
Code language: CSS (css)

This results in only repainting the letter that is changing:

Optimised repaint.

I applied similar techniques to other elements. For example, the @keyframes animations for the clouds was initialy changing the left property to create the movement:

@keyframes moveCloud1 {
    0% { left: 5%;}
    50%{ left : 20%;}
    100%{ left: 5%;}
}
Code language: CSS (css)

This resulted in a repaint in each frame, so I changed it to translate transformations to fix it:

@keyframes moveCloud1 {
    0% { transform: translate3d(5%, 0, 0);}
    50%{ transform: translate3d(30%, 10px, 0);}
    100%{ transform: translate3d(5%, 0, 0);}
}
Code language: CSS (css)

Creating explosions and fire

The game has 3 animations: the city and missile explosions and the fire behind the cities. And dealing with them was a bit more difficult than what I expected.

HTML game explosions.

I started by using GIF animations, it looked like the faster approach for it. But… it had a few problems:

  • The GIF format supports a pallete of only 256 colors. This means image quality isn’t great and graduated transparencies are simply not supported.

  • Developers can’t have direct control over GIF animations. This means, we have to use ugly hacks like adding a diffrent src value with a different param name at the end (ie ?v=222) to get the gift to play again from the beginning.

  • Using the previous hack would cause the load of a new resource, meaning it would take some time to load it, and when having to display an explosion or a missile in a matter of milliseconds, this was a big issue.

  • And on top of that the hack won’t work under certain circunstances, like having mulitple instances of the same image in the same page.

After playing with GIF images, GIF background images, creating mulitple images for different instances etc, I came to the conclusion that GIF was not the right way to go for it. Very buggy.

I finally went for the sprite image approach. Which is basically the same idea behind the old TV carttoons. A sequence of images played very fast one after another to create the illusion of movement.

Explosion animation sprite.

I only had to to use @keyframes in order to create the animation and voila!! Now I was able to get total control over the animation, the different instances of the same animation and even better quality for them as the sprite image could be in any format, including our lovely PNG with beautiful transparencies.

.explosion.active{
    display: block;
    animation: explosionX 200ms steps(3) infinite,
               explosionY 600ms steps(3) infinite;
}
@keyframes explosionX {
   from{ background-position-x: 0; }
   to{ background-position-x: -320px; }
}
@keyframes explosionY {
   from{ background-color: 0;- }
   to{ background-position-y: -320px; }
}
Code language: CSS (css)

Here’s a codepen with one of them if you are curios about the code.

Adding extra movement to the game

I had to be careful regarding how much animation I wanted to add, as I didn’t want it to cause many repaints. However, I felt like the game needed some more animations in order to seem a bit less static.

Each time a city explodes, I make the whole screen shake by using a @keyframes animation I got from this project.

City explosion shaking

When the score gets updated I made the numbers change with a Javascript based animation and I added a star that bounces so the player can notice the score changes every time a word gets destroyed.

Game score animation

Then I added some clouds using a @keyframes animation and a fire sprite when the city is burning.

And for the final touch, icons on thfe game have another wavy animation that in combination with a small sound creates a totally different impression.

Icons wave animation

This was a simple @keyframes animation too that I found somewhere on the web.

Adding sounds

This was quite straight forward. I created an Audio object per each of the sounds the game has.
I store them in an object and then I play them whenever I need them.

var g_sounds = {
    'hover': null,
    'gameOver': null,
    'background': null,
    'newLevel': null,
    'destroyWord': null,
    'noLetter': null,
    'hit': null,
    'missile': null,
    'city-explosion': null
};

createSounds();

function createSounds(){
    for(var key in g_sounds){
        createSound(key);
    }
}

function createSound(fileName){
    var extension = g_isMp3Supported ? '.ogg' : '.mp3';
    var sound = new Audio('audio/' + fileName + extension);

    g_sounds[fileName] = sound;
}
Code language: JavaScript (javascript)

Then basically I would get the sound I need and call sound.cloneNode(true).play();.
The cloneNode part allows me to to play the same sound over itself. So, if I there are multiple missiles or explosions on the screen at the same time, one sound won’t pause when starting the other.

This works well everywhere except in Safari, which causes important performance issues.

Creating a ranking

I though the way would be much more interesting if people can compet against each other somehow.
I didn’t want anyting over complicated for such a side project, so I though a simple ranking will do it.

Once the game ends the user can enter the username and password and save their score in the ranking.

Game ranking.

The backend is based in PHP and MySQL. I use ajax to connect to the backend and then I used the datatables library to display the score and provide the filtering and sorting features.

Of course, this can be done in many different ways. I used what it was easier for me at the moment.

Open sourcing it

The game is totally open sourced, so please feel free to play with it and take a look at the code.

I’m pretty sure I missed many things that can be improved, so I’m totally open to pull requests! And hey! If you want to created your own mod, fork it and share it with me! 🙂 I’ll be happy to see what you achieve!

Was this page helpful?