{
    "name": "Low Res",
    "type": "static",
    "passes": [
      {
        "name": "lowRes",
        "inputs": [
          "default"
        ],
       "glsl": "
      varying vec2 sourceTextureCoordinate;
      uniform float blockSize;
      uniform float maxColors;
      uniform float aberration;
      uniform vec2 outputSize;

      void main() {

        vec2 uv = sourceTextureCoordinate;

        // ensure pixel size looks the same in all resolutions (1080, 2K)
        float bs = blockSize * outputSize.y / 1920.0;

        vec2 st = (uv - 0.5) * outputSize;
        vec2 pixelatedST = floor(st / bs) * bs + bs * 0.5;
        vec2 pixelatedUV = pixelatedST / outputSize + 0.5;

        vec4 baseR = sampleInput(pixelatedUV + aberration);
        vec4 baseG = sampleInput(pixelatedUV);
        vec4 baseB = sampleInput(pixelatedUV - aberration);
        vec4 base = vec4(baseR.r, baseG.g, baseB.b, baseR.a);

        // 8-bit color
        vec3 color_resolution = vec3(8.0, 8.0, 4.0) * maxColors / 256.0;
        vec3 color_bands = floor(base.rgb * color_resolution) / (color_resolution - 1.0);
        vec4 color = vec4(min(color_bands, 1.0), base.a);

        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 input_size;
  float2 output_size;
  float maxColors;
  float blockSize;
  float aberration;
 } Uniforms;
fragment fragmentOut fragmentShader(fragmentIn in [[stage_in]],
 constant Uniforms & uniforms [[buffer(1)]],
 DefaultInputs defaultInputs) {

  float2 uv = in.sourceTextureCoordinate;

  // ensure pixel size looks the same in all resolutions (1080, 2K)
  float bs = uniforms.blockSize * uniforms.output_size.y / 1920.0;

  float2 st = (uv - 0.5) * uniforms.output_size;
  float2 pixelatedST = floor(st / bs) * bs + bs * 0.5;
  float2 pixelatedUV = pixelatedST / uniforms.output_size + 0.5;

  float4 baseR = sampleInput(defaultInputs, pixelatedUV + uniforms.aberration);
  float4 baseG = sampleInput(defaultInputs, pixelatedUV);
  float4 baseB = sampleInput(defaultInputs, pixelatedUV - uniforms.aberration);
  float4 base = float4(baseR.r, baseG.g, baseB.b, baseR.a);

  // 8-bit color
  float3 color_resolution = float3(8.0, 8.0, 4.0) * uniforms.maxColors / 256.0;
  float3 color_bands = floor(base.rgb * color_resolution) / (color_resolution - 1.0);
  float4 color = float4(min(color_bands, 1.0), base.a);

  return {toOutputFormat(color)};
}
        ",
        "uniforms": {
          "blockSize": {
            "value": 5.0
          },
          "maxColors": {
            "value": 256.0
          },
          "aberration": {
            "value": 0.004
          },
          "strength": {
            "min": 0,
            "max": 1,
            "value": 1.0
          }
        }
      }
    ]
}
