Barrier Grid Animation: Illusions on the Web Part 2

This is the second in an article series discussing different optical illusions & mechanical toys and how we can recreate them on the web (and improve them).

Due to the nature of this method, some animations shown here will be fast and fill large screen space. All animations are paused by default. Also, the techniques here require mix-blend-mode support, so as of this writing Edge will not be supported.

In various forms over the years, I have seen a technique to create animation in books or other non-digital and non-video forms. They go by many different names (Kinegrams, Scanimation®, stereographs), but for this discussion we will use the term “Barrier Grid Animation” as it describes the main shared component to create the illusion of motion.

See the Pen Illusion of Motion Basic by Dan Wilson (@danwilson) on CodePen.

In the non-digital world, this consists of a transparent sheet/cel with evenly spaced black lines on top of a second layer consisting of a seemingly chopped image. While not required, most of the time we are talking about vertical lines, so that is what we will assume.

See the Pen Illusion of Motion Split by Dan Wilson (@danwilson) on CodePen.

With just these two physical elements you can create several frames of animation. By only moving the topmost layer the underlying collection of seemingly random lines take shape and animate.

Constructing the Layers

Both layers follow a pattern based on the width of a bar. For simplifying the math, we will use 1px as our base unit. Our first example had five frames of a pink ball moving around. For such an animation, our top layer (the barrier) should consist of a 1px wide transparent bar followed by 4px of a black bar, repeated.

The bottom layer (consisting of the animation’s frames) will be 1px wide of the first frame, followed by 1px of the second frame, etc. This also is repeated, so when our barrier grid is overlaid the same frame will be visible through the small transparent bar, while the other four are covered. The following example demonstrates this (zoomed in for clarification) with each color representing a different frame.

See the Pen Illusion of Motion Zoom by Dan Wilson (@danwilson) on CodePen.

As we move the top layer left and right we will see effectively one frame at a time in sequence. We are blocking out a majority of each image, but our brain will do its best to fill in the missing pieces as the animation progresses by moving the top layer.

We want a pattern for our image where every fifth pixel is from frame 1, every fifth pixel plus one shows frame 2, etc. We will take the following markup where each unnamed div represents a frame (though the div.barrier could also be implemented as a pseudoelement):

<main>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div class="cover"></div>
</main>

The cover boils down to a linear gradient to the right with 1px transparency and 4px black. I use CSS Variables to adapt it as I go, such as if I want to quickly see what happens if I change the base unit to 2px.

.cover {
  background: 
    linear-gradient(to right, 
      transparent 0px,
      transparent var(--pixel), /* 1px */
      var(--cover) var(--pixel), 
      var(--cover) calc(var(--pixel) * 5)); /* 5px = 1 + 4 */
  background-size: calc(var(--pixel) * 5) 100%; /* repeat horizontally */
}
:root {
  --pixel: 1px;
  --cover: black;
}

The hardest part of setting up the barrier grid animation is creating the base image with the chopped frames. However, with blend modes and repeating linear gradients we can accomplish it with CSS.

Chopping the Frames

We can take an image and prep it to be a frame in our animation by using multiple backgrounds — the topmost background being a repeating linear gradient on top of the image we want to chop.

See the Pen Repeating Linear Gradient Over an Image by Dan Wilson (@danwilson) on CodePen.

To get our second frame we apply the same gradient, but we adjust the background-position. You can use a preprocessor to adjust this offset without repeating code, though we are only dealing with a few frames so the resulting CSS would be:

div {
  background-position: 0px 0px;
}
div:nth-of-type(2) {
  background-position-x: var(--pixel);
}
div:nth-of-type(3) {
  background-position-x: calc(var(--pixel) * 2);
}
div:nth-of-type(4) {
  background-position-x: calc(var(--pixel) * 3);
}
div:nth-of-type(5) {
  background-position-x: calc(var(--pixel) * 4);
}

We need to stack our frames over each other so we can position them all as absolute, but we will only see the last frame at this point since we do not have anything with transparency on our frames. Luckily we chose white as our gradient color which will pair nicely with a mix-blend-mode: multiply setting.

See the Pen Repeating Linear Gradient Over an Image with Transparency by Dan Wilson (@danwilson) on CodePen.

A whole series could be written on blend modes, or even simply on multiplying colors. The quick-ish version is that for multiple overlapping elements, we look at each pixel and perform a function to blend the colors on each layer at that pixel. More specifically, the functions happen on each of the three RGB channel values, mapping to a range of 0 to 1 (so a value of 255 in a channel would equal 1). With multiplication, whenever we have any color multiplied by black (with an R,G,B of 0,0,0), the resulting color will always be black. This comes from standard mathematics where multiplying any number by zero will result in zero.

black x red
rgb(0,0,0) x rgb(1,0,0)
rgb(0 x 1, 0 x 0, 0 x 0) = rgb(0,0,0);

Similarly, multiplying by white is like multiplying a number by one. Any number times one is equal to that same number, so any color multiplied by white will equal the same color.

white x red
rgb(1,1,1) x rgb(1,0,0)
rgb(1 x 1, 1 x 0, 1 x 0) = rgb(1,0,0);

With the math out of the way, we can see how applying the blend mode of multiply can let the white bars of each frame effectively become transparent by letting anything that is not white be visible. Since each frame is offset by 1px all frames are seen at once and our image is officially “chopped.”

See the Pen Illusion of Motion (How To Animation) by Dan Wilson (@danwilson) on CodePen.

Bringing it all together

With the barrier constructed and the underlying animation images chopped, we are able to create our animation by moving our topmost layer horizontally. You can either do this with a fixed animation, tie into pointer events where you must drag the barrier layer, or tying it into the accelerometer to get that real old school magical vibe. I show these last two techniques in this full demo (for iOS or other devices that do not allow iframes to access accelerometer, you can view the direct demo)

See the Pen Illusion of Motion by Dan Wilson (@danwilson) on CodePen.

Are there better ways to achieve animation on the web?

No.

Okay, fine. This technique grew out of putting animation into books and small toy packages. We have actual animation in CSS where the browser can fill in the frames for us, and we have the easing option steps() (with future improvements coming) for cases where we want to specify frames more granularly.

But… this was at least fun, right?