Creative WebGL Image Transitions

A set of interesting looking image transitions including distortion and warp effects made with WebGL.

Everybody loves images. They are bright and colorful and we can do fun things with them. Even text sometimes strives to be an image:

     |\_/|                  
     | @ @   Woof! 
     |   <>              _  
     |  _/\------____ ((| |))
     |               `--' |   
 ____|_       ___|   |___.' 
/_/_____/____/_______|

Once you want to show more than one image, you can’t help making a transition between them. Or is it just me?

Jokes aside, image transitions are all over the web. They can be powered by CSS, SVG or WebGL. But of course, the most efficient way to work with graphics in the browser is using the Graphics Processor, or GPU. And the the best way to do this is with WebGL, specifically with shaders written in GLSL.

Today we want to show you some interesting image transition experiments that reveal the boundless possibilities of WebGL. These effects were inspired by the countless incredible design examples and effects seen on websites like The Avener and Oversize Studio.

Setup

I will be using the Three.js framework for my transitions. It doesn’t really matter what library you use, it could have also been the amazing Pixi.js library, or simply (but not so straightforward) native WebGL. I’ve used native WebGL in my previous experiment, so this time I’m going to use Three.js. It also seems most beginner friendly to me.

Three.js uses concepts like Camera, Scene and Objects. We will create a simple Plane object, add it to Scene and put it in front of the Camera, so that it is the only thing that you can see. There is a template for that kind of object, PlaneBufferGeometry:

To cover the whole screen with a plane you need a little bit of geometry. The Camera has a fov (field of view), and the plane has a size. So with some calculations you can get it to fill your whole screen:

camera.fov = 2*(180/Math.PI)*Math.atan(PlaneSize/(2*CameraDistance));

Looks complicated, but it’s just getting the angle(fov), knowing all the distances here:

That is actually the end of the 3D part, everything else will be happening in 2D.

GLSL

In case you are not yet familiar with this language, I highly advise you to check out the wonderful Book Of Shaders.

So, we have a plane and we have a fragment shader attached to it that calculates each pixels color. How do we make a transition? The simplest one done with a shader looks like this:

void main() {
  vec4 image1 = texture2D(texture1,uv);
  vec4 image2 = texture2D(texture2,uv);
  gl_FragColor = mix(image1, image2, progress);
}

Where progress is a number between 0 and 1, indicating the progress of the animation.

With that kind of code you will get a simple fade transition between images. But that’s not that cool, right?

Cool transitions

Usually all transitions are based on changing so called UVs, or the way texture is wrapped on the plane. So for instance, multiplying UV scales the image, adding a number just shifts the image on the plane.

UVs are nothing magical. Think of them as a coordinate system for pixels on a plane:

Let’s start with some basic code:

gl_FragColor = texture2D(texture,uv);

This just shows an image on the screen. Now let’s adjust that code a bit:

gl_FragColor = texture2D(texture,fract(uv + uv));

By taking the fractional part, we make sure that all the values stay between 0 and 1. And if UV was from 0 to 1, doubling it means it will be from 0 to 2, so we should see the fractional part changing from 0 to 1, and from 0 to 1 again!

And that’s what you get: a repeated image. Now let’s try something different: subtracting UV and using the progress for the animation:

gl_FragColor = texture2D(texture, uv - uv * vec2(1.,0) * progress * 0.5);

First, we make sure that we are only changing one axis of UV, by multiplying it with vec2(1.,0). So, when the progress is 0, it should be the default image. Let’s see:

Now we can stretch the image! Let’s combine those two effects into one.

gl_FragColor = texture2D(uTextureOne, uv - fract(uv * vec2(5.,0.)) * progress * 0.1 );

So basically, we do the stretching and repeat it 5 times. We could use any other number as well.

Much better! Next, if we add another image, we get the effect that you can see in demo 7.

Cool isn’t it? Just two simple arithmetic operations, and you get an interesting transition effect.

That’s just one way of changing UVs. Check out all the other demos, and try to guess what’s the math behind them! Try to come up with your own unique animation and share it with me!

References and Credits

Yuri Artiukh

Yuriy is a developer from Kyiv, Ukraine. Leading a small frontend agency riverco.de, also speaking at conferences, and open for freelance projects. Curious about CSS and shaders. Loves to learn every day.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 13

Comments are closed.
  1. I lvoe these effects, would be so cool if there was a tutorial on how to make this transtiion on drag in a infinite slider of sorts! Any ideas on how I could implement this?

    • all of the transitions are just 0 to 1 animations, so if you use same arithmetic “API” in your slider, it would just work.

    • Hello,

      You just need to change the revelative path URL to absolute path URL

      Hope this helps !

  2. Hello,

    First of all thanks to you Yuriy !

    I’ve a question, is there a way to create the transition animation on div element instead only the image ?

    For example I would like to create the transition from slider_1 to slider_2 etc.

    Thanks in advance for your help !

    • Sorry but my comment (code) was deleted.

      If we consider that we have 3 divs with this following IDs “slider_1”, “slider_2”, “slider_3”

      How can I create the transition from slider_1 to slider_2 etc ?

      Actually we can only create this transition on the images.

      Thanks in advance

  3. I upload the files on my server but show this message on my console “Failed to load resource: the server responded with a status of 404 (Not Found)”… I just copy and paste your folder… ]

    I am not a developer, for this reason, would be great if you can tell me exactly which file and what I have to change to make the URL works.