{
  "name": "Frame tilt (LoFi Scale)",
  "type": "dynamic",
  "curve": "Quintic Out",
  "passes": [
    {
      "name": "rotate in",
      "inputs": [
        "default"
      ],
      "glsl": "precision highp float;

               varying vec2 sourceTextureCoordinate;

               uniform vec2 outputSize; // size of the output fbo

               // default uniforms for transitions and video effects
               uniform float progress; // normalized 0-1

               #define PI 3.141592653589793

               mat2 rotate2d(float a){
                 float c = cos(a), s = sin(a);
                 return mat2(c, -s, s, c);
               }

               // effect uniforms
               void main() {

                 vec2 uv = sourceTextureCoordinate;
                 vec2 center = vec2(0.5);
                 vec2 ratio = vec2(outputSize.xy / outputSize.y);

                 float angle = mix(15.0 * (PI / 180.0), 0.0, progress);
                 float scale = mix(0.15, 1.0, progress);
                 center.x = 0.5 - (0.2 * progress);

                 vec2 p = uv - center;
                 p *= ratio;
                 p = rotate2d(angle) * p;
                 p /= scale;
                 p /= ratio;
                 p += center;


                 float m = step(0.0, p.x) * step(p.x, 1.0) * step(0.0, p.y) * step(p.y, 1.0);
                 vec4 base = sampleInput(p);
                 vec4 color = vec4(mix(vec3(0.0), base.rgb, m),1.0);
                 gl_FragColor = toOutputFormat(color);
               }
",
    "metal": "
          using namespace metal;
          struct fragmentIn { float2 sourceTextureCoordinate [[user(locn1)]]; };
          struct fragmentOut { float4 _gl_FragColor [[color(0)]]; };
          typedef struct {
            float4x4 content_transform;
            float4x4 texture_transform;
            float strength;
            float progress;
            float time_sec;
            float duration_sec;
            float clip_duration_sec;
            float2 inputSize;
            float2 outputSize;
            float2 tex0Size;
          } Uniforms;

          fragment fragmentOut fragmentShader(
            fragmentIn in [[stage_in]],
            constant Uniforms & uniforms [[buffer(1)]],
            DefaultInputs defaultInputs) {

            fragmentOut out = {};
            float2 uv = in.sourceTextureCoordinate;
            float2 center = float2(0.5);
            float2 ratio = float2(uniforms.outputSize.xy / uniforms.outputSize.y);

            float angle = mix(15.0 * (M_PI_F / 180.0), 0.0, uniforms.progress);
            float scale = mix(0.15, 1.0, uniforms.progress);
            center.x = 0.5 - (0.2 * uniforms.progress);

            float2 p = uv - center;
            p *= ratio;
            p = float2x2(cos(angle), -sin(angle), sin(angle), cos(angle)) * p;
            p /= scale;
            p /= ratio;
            p += center;

            float m = step(0.0, p.x) * step(p.x, 1.0) * step(0.0, p.y) * step(p.y, 1.0);
            float4 base = sampleInput(defaultInputs, p);
            float4 color = float4(mix(float3(0.0), base.rgb, m), 1.0);
            out._gl_FragColor = toOutputFormat(color);
            return out;
        }
    ",
      "uniforms": {}
    }
  ]
}
