CSS blocky people making waves

It all started with the code challenge in our office. The weekly theme was waves, and I tried to think outside of the box. After dropping a few ideas, I remembered the waves people do on stadiums (According to Wikipedia these are known as Mexican or stadium waves) .

So I made waves using CSS only (code is available on CodePen):

I started without a real idea on how to do it. Obviously, I needed to create some people and animate them. 3D people would be nice, but as this was a weekly code challenge, I had to keep it simple. Blocky people, similar to characters in Minecraft, seemed like a good idea. Making them using Three.js felt too obvious, as I wanted something a little bit more unconventional and fun.

CSS was definitely not made for something like this, which is why I wanted to try it out.


Blocky people are obviously made of blocks, so I needed to build some 3D boxes in CSS. There are many tutorials on creating a 3D box in CSS, so I'll try to keep this short.

We'll need six elements to use as sides of the box. Each side needs to be appropriately sized, rotated and translated into its place.

Here you can see the three steps in creating a CSS box:

  • Size the sides appropriately
  • Translate them into position
  • Rotate them

The box wrapper element is outlined in red and sides are outlined in blue.

If you want to play with it, I created a standalone CodePen. As a bonus, here is a three-sided prism as well.


Each box consists of seven HTML elements (a wrapper and six sides). And each person ended up being built from twelve boxes and some additional wrappers. That means that each person requires a lot of HTML to be written.

To simplify things, we can use WebComponents. Web components are a pretty powerful tool, but in this case, we will only use their template capability.

To do so, we need to extend HTMLElement and set its innerHTML to include our box boilerplate code:

// Box WebComponent
class Box extends HTMLElement {
  constructor() {

    this.innerHTML =
      `<div class="front"></div>
      <div class="back"></div>
      <div class="right"></div>
      <div class="left"></div>
      <div class="bottom"></div>
      <div class="top"></div>`;

// Register out web component so we can use it as a <css-box> tag
window.customElements.define("css-box", Box);

Once we create our web component, we can simply use it like this:

<css-box class="head"></css-box>

Very nifty, ain't it? If you inspect it, you'll see it contains six div elements we created.


Now we can finally build our first person! I already mentioned that it would consist of twelve boxes and some wrapper elements. The boxes are:

  • Head
  • Torso
  • Arms, two segments each x2
  • Legs, two segments each x2
  • Feet x2

As with the box, we'll create <css-person> web component to reduce the HTML boilerplate.

After rotating and putting all of the boxes in place, we end up with a sitting person:


I wanted to automate coloring instead of manually choosing colors for each person. For that, I reached out for SASS' random function. The function will return an integer between zero and the limit given.

We can use it to get a random index from an array like this:

// A list of colors
$colors: red, blue, yellow, green;

// Pick a random index between 0 and 3
$random-index: random(length($colors));

But that gives us an index and not the color. To pick a color value, we'll use nth function, which returns the nth item from the array. Putting it all together, it looks like this:

// A list of colors
$colors: red, blue, yellow, green;

// Picking one random color from the list
$color: nth($colors, random(length($colors)));

Actual code used looks like this:

Unfortunately, SASS is not dynamic and all the randomness is executed once on the build time. When we refresh the page, the colors will stay the same. But if we compile SASS again, we will get a new set of color combinations. That's why I'm using a @for loop to generate a bunch of different color combinations.

You can see the result and loop through the generated combinations below:


To figure out the moves, I looked at a video of people doing stadium waves.

At first glance, it might not look so, but animation code is reasonably simple. It is just a bunch of CSS transforms. To give you an idea, here is how arm segments are animated:

@keyframes arm-top {
  0% {
    transform: rotateX(-40deg);
  50% {
    transform: rotateX(0deg);

@keyframes arm-bottom {
  0% {
    transform: rotateX(40deg);
  50% {
    transform: rotateX(0deg) translateZ(0em);

All of the animations last for the same time, and they are looping indefinitely:

Putting it all together

We created a 3D box, assembled a person from a bunch of them, colored them and animated everything. All that is left to do is add more people and create wave delay animations for each subsequent person.

And here they are, behold the CSS people making waves:

Bonus for the World Cup

This idea comes from a colleague of mine. When I shared it with him, he responded with this:

This is great for the upcoming world cup!!
You could integrate a live score and have two sides of fans - the one in the lead does the wave 😂

I didn't go that far with the live score, but I did some color theming to create international fans:

Select a country to root for:

Final thoughts

One note of performance - I limited demos to six people because if more are added, animations can get very choppy on older hardware and some browsers. Safari behaves the best, and it can handle the most people. That's why on the CodePen, I check for it and add more people on Safari for a better effect. Also, in the post, animations are paused when not in the viewport.

I made it year and a half ago and kind of forgot about it. So I owe big thanks to my colleague Stefan who inspired me to do the World Cup version and write this blog post.

It was super fun to make waves and write this blog post. This is the first time I tried making a more interactive article with multiple demos and controls. I really hope you like it!