26 Nov 2014 CSS

The technique discussed in this article does not apply to IE9 or lower.

Recently, I played the BrainWars game and liked the sonar effect on its Start button. So I decided to apply that effect to my little HTML-based Android game, using just CSS and regular <div> tags.

After a few hours playing around on CodePen, I came up with this.

1. How I made it

The idea is quite simple. Make a circle. Then pulsating another semi-tranparent circle behind it to simulate the spreading waves. And that's pretty easy with CSS.

First, let's create 2 tags for the emitter and the wave:

<div class="sonar-emitter"> <div class="sonar-wave"></div> </div>

To get things position correctly, I let .sonar-wave be the child tag of .sonar-emitter , then applied position: relative to the parent and position: absolute to the child.

Next, decorate the emitter:

.sonar-emitter { position: relative; width: 160px; height: 160px; border-radius: 9999px; background-color: HSL(45,100%,50%); }

The border-radius is set to 9999px to turn the element into a circle (it works!). It's OK to set it to 50% but that won't work on the stock browser of Android 2.3.x. Another way is setting it to half of the width (that is, 80px ) - the catch is that you'll have to update it each time you decide to change the width.

This is what we have:

Now, it's time for the second circle.

.sonar-wave { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 9999px; background-color: HSL(45,100%,50%); z-index: -1; pointer-events: none; }

The shape, size, displaying position, and color are the same as the first circle. Note that I set z-index to -1 to make sure it lies underneath its parent.

Note It is tempting to use the ::after pseudo element as the second circle. However, I prefered a regular element as not all browsers support animation on pseudo elements . Notably, the stock browsers (and apps that rely on WebView) on Android prior to KitKat do not. If you still want the ::after guy, check out my version on CodePen .

OK, let's go to fun part: make things animate! I wanted it to go from 40% semi-transparent to completely transparent while spreading to the scale of 3 times. So here is how I designed the animation keyframe:

@keyframes sonarWave { from { opacity: 0.4; } to { transform: scale(3); opacity: 0; } }

Finally, just assign the keyframe to the second circle:

.sonar-wave { animation: sonarWave 2s linear infinite }

Note that I left out the browser prefixes for the sake of brevity. You could add it manually, use a prefixer, or mixins.

2. Add some flavor

It should already work. However, I decided to add one requirement: our sonar would emit each wave in a different color. That seems hard to achieve with CSS only, so I added some JavaScript.

First, we need a function to generate a random color. I decided to keep the saturation and lightness constant, just randomize the hue.

function colorize() { var hue = Math.random() * 360; return "HSL(" + hue + ",100%,50%)"; }

Then, register an event which updates the wave's color after each pulse.

$(".sonar-wave").on("webkitAnimationIteration oanimationiteration animationiteration", function(){ $(this).css("background-color", colorize()); })

FireFox and IE use the unprefixed animationiteration event name already while others at this time still need prefixes.

That's all. It works on all browsers except IE9 which does not support CSS animation at all. Enjoy!