learn GLSL from zero
ToolsCreate with shadersShader MakerFree online shader builderToolkitShader works ready for your siteSketcher LabPaint your landing’s hero with shaders3D StudioThe Three.js editor in your browser
GamesPlay with shadersOnomatoShaderType a word, blow it upThe WatcherAn eye that follows youAetherLiquid glass with an eye insideBloomcoreA word grows a garden of lightPyrokineThrow fire at the wallVoltaicThe pyramid throws lightningSurfSurf a shader ocean

Lesson 8 of 10 · Shader animation

Seamless loop

Triangle in perfect orbit. The loop that never cuts.

Sometimes you need the animation to repeat exactly, a GIF, a social media loop. No jump, no cut.

The formula: cos(T * 6.2831 / dur) where dur is the duration in seconds. 6.2831 is TAU (2*PI), one full revolution.

The last frame is the first. No cut. No jump.

TAU / durConverts your time to angle. When T = dur the angle is TAU (full revolution). Cosine returns to start. Closed loop.
TryChange 3. (loop duration in seconds) to 1.5 for a faster loop. To 8. for a slow one. It always closes perfectly, the last frame is identical to the first.

The code

void main(){
  vec2 p = (2.*gl_FragCoord.xy - R) / min(R.x, R.y);
  float ph = T * 6.2831 / 3.;
  vec2 c = vec2(cos(ph) * 0.4, sin(ph) * 0.4);
  float ang = ph * 2.;
  float ca = cos(ang), sa = sin(ang);
  vec2 q = p - c; q = vec2(q.x*ca-q.y*sa, q.x*sa+q.y*ca);
  vec2 aq = abs(q);
  float d = max(aq.x*.866+q.y*.5, -q.y) - 0.12;
  vec2 sq=p-c+vec2(.05,-.07); sq=vec2(sq.x*ca-sq.y*sa,sq.x*sa+sq.y*ca);
  vec2 asq=abs(sq);
  float shadow = smoothstep(-.01,.15,max(asq.x*.866+sq.y*.5,-sq.y)-.12);
  float orbit = smoothstep(0.02, 0.0, abs(length(p) - 0.4));
  vec3 col = vec3(0.03, 0.02, 0.04) * shadow;
  col += vec3(0.3, 0.2, 0.15) * orbit * 0.3;
  col += vec3(0.95, 0.45, 0.35) * smoothstep(.01,-.01,d);
  col += vec3(0.8, 0.3, 0.2) * exp(-max(d,0.)*6.) * 0.3;
  col = hueShift(col, 0.0);
  gl_FragColor = vec4(col, 1.);
}
close