{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://video-effects.jingfu.design/settings.schema.json",
  "title": "Video Effects settings",
  "description": "A look for the Video Effects tool (https://video-effects.jingfu.design/). Only version is required: a missing value takes the preset's value, or the neutral default when there is no preset, so {\"version\":1} leaves the image unchanged. Keys are listed group by group; the effects apply by default in this order, which `order` can change: lens distortion, RGB split and chromatic aberration, blur and sharpening, rain on glass, pixelate, grid pattern, ASCII, glitch, exposure and white balance, contrast, saturation and vibrance, bloom, halation, vignette, fade, black and white, colour wheels, curves, LUT, custom shader, scanlines, grain, dither, letterbox, custom component. A LUT (.cube) is never part of the settings: it is loaded on its own, and lut.amount sets its strength; nor is a custom shader's GLSL code, whose strength customShader.amount sets, or a custom component's React code, whose strength customComponent.amount sets. The studio shows the settings as effects, a card each, which appear when a value differs from its default (a LUT, custom shader or custom component effect only while its LUT or code is set: four of each, the LUT and lut2 to lut4, the custom shader and customShader2 to customShader4, the custom component and customComponent2 to customComponent4) and can be added, turned off, removed and dragged into another order, which is `order` (an effect turned off reads at its defaults, the grain seed kept, and a LUT's, custom shader's or custom component's strength at 0, until it is turned on again; while one is off, the studio's URL, Copy link and Copy settings keep the strength set); in their default order, by id with the settings each holds: transform (transform.x, transform.y, transform.scale, transform.rotation); lensDistortion (lensDistortion); rgbSplit (rgbSplit.amount, rgbSplit.angle); chromaticAberration (chromaticAberration); blur (blur, progressiveBlur.amount, progressiveBlur.start, progressiveBlur.angle); sharpen (sharpen.amount, sharpen.radius); rain (rain.amount, rain.size, rain.speed, rain.fog); pixelate (pixelate); pattern (pattern.amount, pattern.style, pattern.size, pattern.minSize, pattern.maxSize, pattern.background); ascii (ascii.amount, ascii.characters, ascii.size, ascii.colour, ascii.background); glitch (glitch.amount, glitch.size, glitch.speed); chromaKey (chromaKey.amount, chromaKey.hue, chromaKey.tolerance, chromaKey.softness, chromaKey.spill, chromaKey.matte); light (exposure, contrast, saturation, vibrance, temperature, tint); bloom (bloom.amount, bloom.threshold, bloom.radius); halation (halation.amount, halation.threshold, halation.radius); vignette (vignette.amount, vignette.midpoint, vignette.roundness, vignette.feather); fade (fade); blackWhite (blackWhite.amount, blackWhite.reverse); colourWheels (shadows.hue, shadows.amount, shadows.brightness, midtones.hue, midtones.amount, midtones.brightness, highlights.hue, highlights.amount, highlights.brightness); curves (curves.lumMix, curves.master, curves.red, curves.green, curves.blue, curves.hueVsHue, curves.hueVsSat, curves.hueVsLum, curves.lumVsSat, curves.satVsSat); lut (lut.amount); lut2 (lut2.amount); lut3 (lut3.amount); lut4 (lut4.amount); customShader (customShader.amount); customShader2 (customShader2.amount); customShader3 (customShader3.amount); customShader4 (customShader4.amount); scanlines (scanlines.amount, scanlines.size); grain (grain.amount, grain.size, grain.seed); dither (dither.amount, dither.style, dither.colour, dither.levels, dither.size); letterbox (letterbox); opacity (opacity); customComponent (customComponent.amount); customComponent2 (customComponent2.amount); customComponent3 (customComponent3.amount); customComponent4 (customComponent4.amount).",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "version"
  ],
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Optional; may point back at this schema."
    },
    "version": {
      "const": 1,
      "description": "Settings format version. A consumer refuses a version it does not know."
    },
    "preset": {
      "enum": [
        "none",
        "soft-glow",
        "warm-film",
        "night-film",
        "black-and-white",
        "faded",
        "vintage-lens",
        "retro-vhs",
        "cinematic"
      ],
      "description": "Start from this look; the values given override it. Presets set every value except grain.seed and the strengths of the LUTs, custom shaders and custom components (lut.amount, customShader.amount, customComponent.amount and their copies', lut2.amount and on), never touch a loaded LUT, custom shader or custom component, and never change within a settings version: none (Every effect off: the original image.); soft-glow (Dreamy highlights: a wide bloom with slightly softer contrast.); warm-film (A warm colour-negative look: muted colour, a little halation, grain and a light vignette.); night-film (Tungsten film at night: cool, contrasty, with strong red halos and bloom round the lights.); black-and-white (Monochrome with firmer contrast, grain and a light vignette.); faded (Low contrast and muted colour, slightly lifted, with fine grain.); vintage-lens (An old, simple lens: colour fringes at the edges, dark corners and a gentle glow.); retro-vhs (A worn videotape on an old TV: red and blue knocked out of line, a soft picture with haloed edges, boosted but faded colour, scanlines and grain.); cinematic (Widescreen cinema: letterbox bars leaving a 2.39:1 picture, firmer contrast, a touch of warmth, a light vignette, fine grain and a little sharpening.)."
    },
    "exposure": {
      "type": "number",
      "minimum": -3,
      "maximum": 3,
      "default": 0,
      "description": "Brightens a dark video or tames an overexposed one, in photographic stops, in linear light: +1 doubles the light and -1 halves it. Unit: stops."
    },
    "contrast": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "Pushes tones away from mid-grey (18% linear), or flattens them toward it: -100 is flat, 0 unchanged, 100 strong."
    },
    "saturation": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "Colour strength, measured against Rec. 709 luminance: -100 is black and white, 0 unchanged, 100 doubles it."
    },
    "vibrance": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "Colour strength that works hardest on muted colours and least on vivid ones, and holds back on skin tones: 100 nearly doubles the colour of a muted pixel and barely touches a vivid one; -100 drains the muted colours first."
    },
    "temperature": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "White balance from cooler, blue (negative) to warmer, amber (positive), without changing brightness."
    },
    "tint": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "White balance from green (negative) to magenta (positive), without changing brightness."
    },
    "fade": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "default": 0,
      "description": "Lifts the blacks to a matte grey, like a faded print, leaving white where it was. By default it applies after the vignette, so the corners fade too; 100 lifts black to about code 56 of 255 (4% linear light)."
    },
    "blackWhite": {
      "type": "object",
      "additionalProperties": false,
      "description": "The picture turned to greys by its brightness, at the strength you choose, or reversed, as a negative.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the picture turns to grey, by its brightness (Rec. 709 luminance in linear light): 0 turns black and white off, 100 leaves no colour. By default it applies after the fade, so the glows, the vignette and the fade turn grey with the picture, and a LUT, scanlines, grain and dither come after it."
        },
        "reverse": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "0 normal; 1 reversed, as a negative: white turns black and black white, the greys turned around on their encoded values, as an image editor's Invert does."
        }
      }
    },
    "shadows": {
      "type": "object",
      "additionalProperties": false,
      "description": "The shadows' colour wheel, a grading app's lift: a colour and a brightness for the dark tones, black moving most and white staying where it is.",
      "properties": {
        "hue": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 0,
          "description": "The colour the shadows are pushed toward, as an angle on the colour wheel: 0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta (360 is red again). It does nothing while shadows.amount is 0. Unit: degrees."
        },
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the shadows are pushed toward the hue, a shift that leaves their brightness (Rec. 709 luma) nearly as it was: 0 none; 100 the most, which takes black about a fifth of the way to the pure colour, white staying white. Unit: %."
        },
        "brightness": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Lifts black (positive) or lowers it (negative), white staying where it is, as a grading app's lift: 100 takes black to code 51 of 255, and -100 crushes every tone below about code 43 to black."
        }
      }
    },
    "midtones": {
      "type": "object",
      "additionalProperties": false,
      "description": "The midtones' colour wheel, a grading app's gamma: a colour and a brightness for the middle tones, black and white staying where they are.",
      "properties": {
        "hue": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 0,
          "description": "The colour the midtones are pushed toward, as an angle on the colour wheel: 0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta (360 is red again). It does nothing while midtones.amount is 0. Unit: degrees."
        },
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the midtones are pushed toward the hue, a shift that leaves their brightness (Rec. 709 luma) nearly as it was: 0 none; 100 the most, which takes a mid-grey red to about (155, 120, 120), black and white staying where they are. Unit: %."
        },
        "brightness": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Brightens the midtones (positive) or darkens them (negative), black and white staying where they are, as a grading app's gamma: 100 takes mid-grey (code 128 of 255) to about 181, and -100 to about 64."
        }
      }
    },
    "highlights": {
      "type": "object",
      "additionalProperties": false,
      "description": "The highlights' colour wheel, a grading app's gain: a colour and a brightness for the bright tones, white moving most and black staying where it is.",
      "properties": {
        "hue": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 0,
          "description": "The colour the highlights are pushed toward, as an angle on the colour wheel: 0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta (360 is red again). It does nothing while highlights.amount is 0. Unit: degrees."
        },
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the highlights are pushed toward the hue, a shift that leaves their brightness (Rec. 709 luma) nearly as it was: 0 none; 100 the most, which takes white toward the pure colour (to about (255, 248, 194) for an orange at 30), black staying black. Unit: %."
        },
        "brightness": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Brightens the highlights (positive) or darkens them (negative), black staying where it is, as a grading app's gain: 100 multiplies the encoded values by 1.5, so every tone above about code 170 of 255 turns white, and -100 halves them, taking white to mid-grey."
        }
      }
    },
    "curves": {
      "type": "object",
      "additionalProperties": false,
      "description": "DaVinci Resolve's curves: the custom curves (the master curve, Resolve's Y, with its Lum mix, then the red, green and blue curves) and the hue curves (Hue vs Hue, Hue vs Sat, Hue vs Lum, Lum vs Sat, Sat vs Sat), each a list of [x, y] points.",
      "properties": {
        "lumMix": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How the master curve (curves.master) works, as DaVinci Resolve's Lum mix: 100 moves the brightness (Rec. 709 luma) alone, keeping each colour's chroma, so an S-curve adds contrast without adding colour; 0 applies the curve to red, green and blue alike, as Photoshop's RGB curve does, which makes colours richer as it adds contrast; between, a mix of the two. It changes nothing while the master curve is the straight line."
        },
        "master": {
          "type": "array",
          "minItems": 2,
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Input, 0 to 100 %; y the Output, 0 to 100 %."
          },
          "default": [
            [
              0,
              0
            ],
            [
              100,
              100
            ]
          ],
          "description": "The master curve, DaVinci Resolve's Y: brightness in to brightness out, first of the curves; curves.lumMix says whether it moves the brightness alone (100) or each channel (0). Points are [x, y] pairs, x the input and y the output, each 0 to 100 % of the code range (0 black, 100 white), at most 16, at least 2; the curve runs through them smoothly, never overshooting them, and is flat past the first and last (a first point at [10, 0] takes every value below 10 % to black). By default [[0, 0], [100, 100]], the straight line that changes nothing."
        },
        "red": {
          "type": "array",
          "minItems": 2,
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Input, 0 to 100 %; y the Output, 0 to 100 %."
          },
          "default": [
            [
              0,
              0
            ],
            [
              100,
              100
            ]
          ],
          "description": "The red curve: the red channel in to out, after the master curve. Raised in the midtones it warms the picture; lowered, it turns it toward cyan. Points are [x, y] pairs, x the input and y the output, each 0 to 100 % of the code range (0 black, 100 white), at most 16, at least 2; the curve runs through them smoothly, never overshooting them, and is flat past the first and last (a first point at [10, 0] takes every value below 10 % to black). By default [[0, 0], [100, 100]], the straight line that changes nothing."
        },
        "green": {
          "type": "array",
          "minItems": 2,
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Input, 0 to 100 %; y the Output, 0 to 100 %."
          },
          "default": [
            [
              0,
              0
            ],
            [
              100,
              100
            ]
          ],
          "description": "The green curve: the green channel in to out, after the master curve. Raised it turns the picture toward green; lowered, toward magenta. Points are [x, y] pairs, x the input and y the output, each 0 to 100 % of the code range (0 black, 100 white), at most 16, at least 2; the curve runs through them smoothly, never overshooting them, and is flat past the first and last (a first point at [10, 0] takes every value below 10 % to black). By default [[0, 0], [100, 100]], the straight line that changes nothing."
        },
        "blue": {
          "type": "array",
          "minItems": 2,
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Input, 0 to 100 %; y the Output, 0 to 100 %."
          },
          "default": [
            [
              0,
              0
            ],
            [
              100,
              100
            ]
          ],
          "description": "The blue curve: the blue channel in to out, after the master curve. Raised it cools the picture; lowered, it turns it toward yellow. Points are [x, y] pairs, x the input and y the output, each 0 to 100 % of the code range (0 black, 100 white), at most 16, at least 2; the curve runs through them smoothly, never overshooting them, and is flat past the first and last (a first point at [10, 0] takes every value below 10 % to black). By default [[0, 0], [100, 100]], the straight line that changes nothing."
        },
        "hueVsHue": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Hue, 0 to 360 degrees (round the circle); y the Hue shift, -180 to 180 degrees."
          },
          "default": [],
          "description": "Hue vs Hue, after the custom curves: turns colours of one hue toward another. x is the hue, 0 to 360 degrees round the colour circle as a colour picker has it (0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta, the curve running on from 360 to 0); y the turn, -180 to 180 degrees, positive toward the higher hues, 0 none. The colour's saturation and value (HSV) stay, and greys, which have no hue, stay grey. To turn one hue alone, pin its neighbours at 0: [[90, 0], [120, 30], [150, 0]] turns greens toward cyan. Points are [x, y] pairs, at most 16; empty by default, a flat line at 0, which changes nothing. One point moves the whole line; the curve runs through two or more smoothly, never overshooting them, round the colour circle."
        },
        "hueVsSat": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Hue, 0 to 360 degrees (round the circle); y the Saturation, -100 to 100 %."
          },
          "default": [],
          "description": "Hue vs Sat, after the custom curves: more or less colour by hue. x is the hue (0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta, the curve running on from 360 to 0); y the change in saturation, -100 (grey) to 100 (twice the colour), 0 none. The colour is scaled round its luma, so its brightness stays. [[0, 0], [120, -60], [240, 0]] mutes the greens and yellows. Points are [x, y] pairs, at most 16; empty by default, a flat line at 0, which changes nothing. One point moves the whole line; the curve runs through two or more smoothly, never overshooting them, round the colour circle."
        },
        "hueVsLum": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Hue, 0 to 360 degrees (round the circle); y the Brightness, -100 to 100 %."
          },
          "default": [],
          "description": "Hue vs Lum, after the custom curves: brighter or darker by hue. x is the hue (0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta, the curve running on from 360 to 0); y -100 to 100: at 100 a fully saturated colour's luma doubles (a stop brighter), at -100 it halves, a paler colour moves less and a grey not at all, its chroma kept. [[180, 0], [220, -50], [260, 0]] darkens a blue sky. Points are [x, y] pairs, at most 16; empty by default, a flat line at 0, which changes nothing. One point moves the whole line; the curve runs through two or more smoothly, never overshooting them, round the colour circle."
        },
        "lumVsSat": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Luma, 0 to 100 %; y the Saturation, -100 to 100 %."
          },
          "default": [],
          "description": "Lum vs Sat, after the custom curves: more or less colour by brightness. x is the luma (Rec. 709 on the encoded values), 0 black to 100 white; y the change in saturation, -100 (grey) to 100 (twice the colour), 0 none. [[0, -100], [30, 0]] takes the colour out of the deep shadows. Points are [x, y] pairs, at most 16; empty by default, a flat line at 0, which changes nothing. One point moves the whole line; the curve runs through two or more smoothly, never overshooting them, flat past the first and last."
        },
        "satVsSat": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2,
            "description": "A point: [x, y], x the Saturation, 0 to 100 %; y the Saturation, -100 to 100 %."
          },
          "default": [],
          "description": "Sat vs Sat, after the custom curves: more or less colour by how colourful a pixel already is. x is its saturation (HSV), 0 grey to 100 a pure colour; y the change, -100 (grey) to 100 (twice the colour), 0 none. [[0, 0], [40, 40], [100, 0]] lifts the muted colours and leaves the vivid ones. Points are [x, y] pairs, at most 16; empty by default, a flat line at 0, which changes nothing. One point moves the whole line; the curve runs through two or more smoothly, never overshooting them, flat past the first and last."
        }
      }
    },
    "lut": {
      "type": "object",
      "additionalProperties": false,
      "description": "A creative look-up table (a .cube file) applied to the graded image. The table itself is loaded separately (window.videoEffects.loadLut, the WebMCP tool video_effects_load_lut, or the studio's LUT control) and is never part of the settings or a link: only its strength is.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the loaded LUT's look is applied, mixed on the sRGB-encoded values the LUT reads and writes: 100 is the LUT as made, 50 halfway between the image without it and with it, 0 none. It changes nothing while no LUT is loaded, and presets leave it, and the LUT, as they are. Unit: %."
        }
      }
    },
    "lut2": {
      "type": "object",
      "additionalProperties": false,
      "description": "A creative look-up table (a .cube file) applied to the graded image. The table itself is loaded separately (window.videoEffects.loadLut, the WebMCP tool video_effects_load_lut, or the studio's LUT control) and is never part of the settings or a link: only its strength is. This is the look's LUT 2.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the loaded LUT's look is applied, mixed on the sRGB-encoded values the LUT reads and writes: 100 is the LUT as made, 50 halfway between the image without it and with it, 0 none. It changes nothing while no LUT is loaded, and presets leave it, and the LUT, as they are. Unit: %."
        }
      }
    },
    "lut3": {
      "type": "object",
      "additionalProperties": false,
      "description": "A creative look-up table (a .cube file) applied to the graded image. The table itself is loaded separately (window.videoEffects.loadLut, the WebMCP tool video_effects_load_lut, or the studio's LUT control) and is never part of the settings or a link: only its strength is. This is the look's LUT 3.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the loaded LUT's look is applied, mixed on the sRGB-encoded values the LUT reads and writes: 100 is the LUT as made, 50 halfway between the image without it and with it, 0 none. It changes nothing while no LUT is loaded, and presets leave it, and the LUT, as they are. Unit: %."
        }
      }
    },
    "lut4": {
      "type": "object",
      "additionalProperties": false,
      "description": "A creative look-up table (a .cube file) applied to the graded image. The table itself is loaded separately (window.videoEffects.loadLut, the WebMCP tool video_effects_load_lut, or the studio's LUT control) and is never part of the settings or a link: only its strength is. This is the look's LUT 4.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the loaded LUT's look is applied, mixed on the sRGB-encoded values the LUT reads and writes: 100 is the LUT as made, 50 halfway between the image without it and with it, 0 none. It changes nothing while no LUT is loaded, and presets leave it, and the LUT, as they are. Unit: %."
        }
      }
    },
    "sharpen": {
      "type": "object",
      "additionalProperties": false,
      "description": "An unsharp mask on brightness, which raises the contrast of edges and fine detail.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Strength of an unsharp mask on brightness (colours aren't sharpened, so edges get no colour fringes), worked out in perceptual (sRGB-encoded) values; 0 turns sharpening off and 100 is strong."
        },
        "radius": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 5,
          "default": 1,
          "description": "Size of the detail sharpened (the unsharp mask's blur), in pixels on a 1080-line frame, scaled with the resolution: small values crisp fine texture, larger ones add local contrast. Unit: px at 1080p."
        }
      }
    },
    "blur": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "default": 0,
      "description": "A soft blur of the frame, in linear light, even unless progressiveBlur.amount makes it grow across the frame; 0 is off and 100 blurs with a radius of about 2% of the frame's shorter side (a Gaussian whose standard deviation is 1% of it). The radius grows faster toward the top of the range, so the low values give fine control."
    },
    "progressiveBlur": {
      "type": "object",
      "additionalProperties": false,
      "description": "Makes the blur grow across the frame, from sharp to its full strength, rather than even: where it starts, over how much of the frame it grows, and toward which side.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Makes the blur progressive: the share of the frame, along its direction, over which it grows smoothly from sharp to its full strength. 0 blurs the whole frame evenly; 100 grows it across the whole frame. Unit: %."
        },
        "start": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Where a progressive blur begins: the share of the frame kept sharp, from the side it grows away from. Changes nothing while the blur is even. Unit: %."
        },
        "angle": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 90,
          "description": "Which way a progressive blur grows, counter-clockwise on screen: 90 is sharp at the bottom and blurred at the top, 270 the other way round, 0 blurred toward the right. Changes nothing while the blur is even. Unit: degrees."
        }
      }
    },
    "bloom": {
      "type": "object",
      "additionalProperties": false,
      "description": "A soft glow that spreads from the brightest parts of the frame, as strong light does in a lens or the eye.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Strength of the glow; 0 turns bloom off."
        },
        "threshold": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "default": 0.7,
          "description": "How bright a pixel must be to glow, in linear-light luminance, from 0 (everything) to 1 (only the source's white). The knee is soft. Unit: linear luminance."
        },
        "radius": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 25,
          "default": 4,
          "description": "How far the glow spreads, in percent of the frame's shorter side, so it looks the same at any resolution. Unit: % of the shorter side."
        }
      }
    },
    "halation": {
      "type": "object",
      "additionalProperties": false,
      "description": "The red-orange halo film shows round bright highlights, from light reflecting back through the film base.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Strength of the red-orange halo; 0 turns halation off."
        },
        "threshold": {
          "type": "number",
          "minimum": 0,
          "maximum": 1,
          "default": 0.8,
          "description": "How bright a highlight must be to halate, in linear-light luminance from 0 to 1. The halo shows round the highlight, not over it. Unit: linear luminance."
        },
        "radius": {
          "type": "number",
          "minimum": 0.2,
          "maximum": 10,
          "default": 1.5,
          "description": "How far the halo reaches beyond the highlight, in percent of the frame's shorter side. Unit: % of the shorter side."
        }
      }
    },
    "lensDistortion": {
      "type": "number",
      "minimum": -100,
      "maximum": 100,
      "default": 0,
      "description": "Bends straight lines as a simple lens does: negative is barrel distortion (lines bow outward, the middle bulges), positive pincushion (lines bow inward). The picture is scaled so it always fills the frame, with no empty corners: at -100 the middle is magnified 1.3 times."
    },
    "chromaticAberration": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "default": 0,
      "description": "Red and blue fringes that grow from nothing at the centre to the frame's edges, as from a simple lens; 100 separates them by 0.5% of the frame's diagonal at the corners."
    },
    "rain": {
      "type": "object",
      "additionalProperties": false,
      "description": "Rain drops on a pane of glass in front of the picture: each drop bends the light through it, the larger ones run down and leave a clear trail, and the glass between them mists over.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How many drops are on the glass, from a few to a downpour; 0 turns the rain off. Each drop bends the picture behind it like a small lens, catches a highlight on its rim and shows the picture sharp through the mist."
        },
        "size": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 5,
          "default": 2.6,
          "description": "Size of the larger drops, their mean width in percent of the frame's shorter side, so they look the same at any resolution; the smaller droplets scale with them. Unit: % of the shorter side."
        },
        "speed": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 40,
          "description": "How fast the larger drops run down the glass, each at its own pace and in fits and starts, leaving a trail of droplets; 0 holds them still. The rain moves with the video's own time, so a paused frame holds still and the export matches the preview; a photo shows one moment of it."
        },
        "fog": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 50,
          "description": "How misty the glass is between the drops: 0 is clear glass, 100 a soft blur of about 1% of the frame's shorter side, a little lifted. The drops and the trails they leave stay clear."
        }
      }
    },
    "vignette": {
      "type": "object",
      "additionalProperties": false,
      "description": "Darkens the frame's edges toward the corners.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How dark the edges get, in linear light; 0 turns the vignette off and 100 takes the corners down about two stops. It only darkens, and with letterbox bars it fits the picture between them."
        },
        "midpoint": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 50,
          "description": "Where the darkening begins, from the centre (0) to the corners (100)."
        },
        "roundness": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Shape of the vignette: -100 follows the frame's rectangle, 0 is an ellipse fitted to the frame and 100 a circle."
        },
        "feather": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 50,
          "description": "Softness of the vignette's edge, from hard (0) to very gradual (100)."
        }
      }
    },
    "rgbSplit": {
      "type": "object",
      "additionalProperties": false,
      "description": "Red and blue shifted apart by the same distance everywhere, in one direction: the glitch or worn-videotape look.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Shifts the red and blue channels apart by the same distance across the whole frame, in one direction, for a glitch or worn-videotape look (chromatic aberration, by contrast, grows from nothing at the centre); 0 is off and 100 separates red from blue by 1.5% of the frame's shorter side."
        },
        "angle": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 0,
          "description": "Direction of the split, counter-clockwise on screen: 0 moves red right and blue left, 90 red up and blue down. Unit: degrees."
        }
      }
    },
    "scanlines": {
      "type": "object",
      "additionalProperties": false,
      "description": "Dark horizontal lines, as on a CRT screen.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Dark horizontal lines, as on a CRT screen, applied to the sRGB-encoded image so they look even from shadows to highlights; 0 is off, and at 100 a dark line's centre keeps about 40% of its brightness (a little more when the lines are only a few pixels apart)."
        },
        "size": {
          "type": "number",
          "minimum": 1,
          "maximum": 8,
          "default": 3,
          "description": "Line period (one dark and one bright line) in pixels on a 1080-line frame, scaled with the resolution. Lines closer than 6 of the frame's own pixels are drawn on whole pixel rows (the period rounded, 2 at least), so they never beat into moiré bands; lines finer than 2 pixels can't be drawn, and fade into an even darkening that keeps the light the lines would average to. Unit: px at 1080p."
        }
      }
    },
    "letterbox": {
      "type": "number",
      "minimum": 0,
      "maximum": 3,
      "default": 0,
      "description": "Pure black cinema bars at the top and bottom, added last by default: the aspect ratio of the picture left between them, its width over its height, so 2.39 leaves a 2.39:1 picture on any frame (1.85:1 is the widescreen of many films, 2.39:1 the anamorphic widescreen of epic cinema). 0 is off, and a frame already as wide as the ratio or wider gets no bars. Unit: width ÷ height."
    },
    "dither": {
      "type": "object",
      "additionalProperties": false,
      "description": "The picture reduced to a few tones per channel, or to a few greys (black and white at two levels), with the in-between tones made of a pattern of dots (ordered, a Bayer matrix) or of noise, as old screens and printers did.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How much of the dithered picture shows over the picture as it was; 0 turns dithering off and 100 shows only the dithered tones. By default it applies after the grain and before letterbox bars, on the sRGB-encoded picture."
        },
        "style": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "How the in-between tones are made: 0 ordered, the regular crosshatch of an 8x8 Bayer matrix, as on old computer screens; 1 noise, an even scatter of dots that changes from frame to frame like grain (the same frame always the same), so a paused frame holds still and the export matches the preview."
        },
        "colour": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "0 dithers the picture's brightness to grey tones (black and white at two levels); 1 dithers red, green and blue each, to a few colours."
        },
        "levels": {
          "type": "integer",
          "minimum": 2,
          "maximum": 16,
          "default": 2,
          "description": "Tones per channel, from 2 (black and white, or eight colours) to 16; the tones between them are made of dots, so the picture keeps its overall light."
        },
        "size": {
          "type": "number",
          "minimum": 1,
          "maximum": 16,
          "default": 2,
          "description": "The size of the dither's dots in pixels on a 1080-line frame, scaled with the resolution and rounded to whole pixels (one at least), so each dot holds one tone and the picture keeps its light at any size: 1 is as fine as a 1080p frame's pixels, larger values give a coarser, retro pattern. Unit: px at 1080p."
        }
      }
    },
    "pixelate": {
      "type": "number",
      "minimum": 0,
      "maximum": 120,
      "default": 0,
      "description": "Big square pixels, each the average colour of the picture it covers, on a grid centred on the frame: their size in pixels on a 1080-line frame, scaled with the resolution; 0 turns pixelation off. Unit: px at 1080p."
    },
    "pattern": {
      "type": "object",
      "additionalProperties": false,
      "description": "The picture sampled on a grid and drawn as a pattern, one mark per cell: dots that grow with the light between a smallest and a largest size, or tiles of the cell's colour; over the picture or on black.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How strongly the pattern is drawn over the picture; 0 turns it off. Tiles at 100 replace the picture; dots are drawn over it (or, with a dark background, in its place)."
        },
        "style": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "The mark drawn in each cell: 0 dots, light dots that grow with the brightness of their cell, as a halftone; 1 tiles, each cell filled with its average colour between dark grid lines. (ASCII characters are an effect of their own, ascii.)"
        },
        "size": {
          "type": "number",
          "minimum": 4,
          "maximum": 80,
          "default": 16,
          "description": "The grid's cell size in pixels on a 1080-line frame, scaled with the resolution, on a grid centred on the frame. Unit: px at 1080p."
        },
        "minSize": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "The dots' smallest size, as a share of their cell's width: the size they have in the darkest cells. At 0 the darkest cells have no dot; above 0 every cell has one. Tiles fill their cells whatever this is. Unit: %."
        },
        "maxSize": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "The dots' largest size, as a share of their cell's width: the size they have in the brightest cells; at 100 they touch the dots beside them. A max below the min counts as the min. Tiles fill their cells whatever this is. Unit: %."
        },
        "background": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the picture behind the pattern darkens, at the pattern's strength: 0 keeps it as it is, the pattern over it; 100, with the amount at 100, leaves only the pattern, on black, hiding the picture (tiles at full strength cover it already)."
        }
      }
    },
    "ascii": {
      "type": "object",
      "additionalProperties": false,
      "description": "The picture drawn in text characters, one per cell of a grid: 0s and 1s at random over the brighter cells, or, chosen by the cell's brightness, a ramp of characters from a dot to an @ or shaded blocks; in white or in the picture's colours, over the picture or on black.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How strongly the characters are drawn; 0 turns ASCII off. At 100 they are drawn at full strength over the picture (or, with a dark background, in its place)."
        },
        "characters": {
          "type": "integer",
          "enum": [
            0,
            1,
            2
          ],
          "minimum": 0,
          "maximum": 2,
          "default": 0,
          "description": "Which characters draw the picture, in a 5x7 pixel font: 0 binary, 0s and 1s at random over most of the brighter cells; 1 classic, a ramp from a dot through - : + = % * # to @ as the cell brightens (none with less ink than the one before), as in ASCII art; 2 blocks, shades from light to solid."
        },
        "size": {
          "type": "number",
          "minimum": 6,
          "maximum": 64,
          "default": 16,
          "description": "The size of each character's cell in pixels on a 1080-line frame, scaled with the resolution, on a grid centred on the frame. Unit: px at 1080p."
        },
        "colour": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "0 draws the characters in white, brighter in brighter cells; 1 in the colour of the picture under each cell."
        },
        "background": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How far the picture behind the characters darkens, at their strength: 0 keeps it as it is, the characters over it; 100, with the amount at 100, leaves only the characters, on black, as ASCII art."
        }
      }
    },
    "glitch": {
      "type": "object",
      "additionalProperties": false,
      "description": "A digital glitch: bands of the picture torn sideways with their colours knocked apart, and blocks of it broken up, changing from moment to moment.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How often and how hard the picture glitches: bands torn sideways with their red and blue knocked apart, and blocks broken up and shifted, in bursts; 0 turns the glitch off."
        },
        "size": {
          "type": "number",
          "minimum": 2,
          "maximum": 120,
          "default": 24,
          "description": "Height of a glitched band in pixels on a 1080-line frame, scaled with the resolution; the broken blocks are a few bands across. Unit: px at 1080p."
        },
        "speed": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 50,
          "description": "How often the glitch changes, from about twice to thirty times a second; 0 holds one glitch still. It moves with the video's own time, so a paused frame holds still and the export matches the preview."
        }
      }
    },
    "grain": {
      "type": "object",
      "additionalProperties": false,
      "description": "Film-like grain, strongest in the midtones, seeded by the frame.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "Grain strength, strongest in the midtones; 0 turns grain off."
        },
        "size": {
          "type": "number",
          "minimum": 0.5,
          "maximum": 3,
          "default": 1,
          "description": "Grain size in pixels on a 1080-line frame, scaled with the resolution, so a 4K export has the same grain as a small preview. Unit: px at 1080p."
        },
        "seed": {
          "type": "integer",
          "minimum": 0,
          "maximum": 2147483647,
          "default": 0,
          "description": "Random seed: change it for a different grain pattern with the same look. The same seed and frame always give the same grain."
        }
      }
    },
    "transform": {
      "type": "object",
      "additionalProperties": false,
      "description": "Where the picture sits in the frame: moved, scaled and turned about its centre, black where it leaves the frame.",
      "properties": {
        "x": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Moves the picture right (positive) or left, by this share of the frame's width: at 100 it has moved a whole frame width and is out of sight. What it uncovers is black. Unit: %."
        },
        "y": {
          "type": "number",
          "minimum": -100,
          "maximum": 100,
          "default": 0,
          "description": "Moves the picture up (positive) or down, by this share of the frame's height: at 100 it has moved a whole frame height and is out of sight. What it uncovers is black. Unit: %."
        },
        "scale": {
          "type": "number",
          "minimum": 10,
          "maximum": 400,
          "default": 100,
          "description": "The picture's size, a share of the frame's, about its centre: under 100 it shrinks with black round it, over 100 it is enlarged and cropped to the frame. Unit: %."
        },
        "rotation": {
          "type": "number",
          "minimum": -180,
          "maximum": 180,
          "default": 0,
          "description": "Turns the picture about its centre, clockwise for positive angles, as Premiere and CapCut turn a clip; the corners it uncovers are black. By default the transform applies before every other effect, so the vignette, the letterbox and the rest stay where the frame has them. Unit: degrees."
        }
      }
    },
    "opacity": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "default": 100,
      "description": "How much of the picture shows over black, as a clip's opacity over an empty background in an editor: 100 is all of it, 0 none. Exports have no transparency, so what shows through is black. Applied last by default, after the letterbox, on the sRGB-encoded values, as editors blend. Unit: %."
    },
    "chromaKey": {
      "type": "object",
      "additionalProperties": false,
      "description": "A colour taken out of the picture to black, as a green or blue screen is keyed, with its spill taken out of what stays.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 0,
          "description": "How fully the key colour is taken out: 0 turns the chroma key off, 100 takes it out to black wherever it matches. Exports have no transparency, so what is keyed out is black. It keys the picture as read, after the other effects that change where it is read or filter it, and before Light and colour, so the grade and the glows work on what stays."
        },
        "hue": {
          "type": "number",
          "minimum": 0,
          "maximum": 360,
          "default": 120,
          "description": "The colour taken out, as an angle on the colour wheel: 0 red, 60 yellow, 120 green, 180 cyan, 240 blue and 300 magenta (360 is red again). A green screen is about 120 to 140, a blue one about 220 to 240. Unit: degrees."
        },
        "tolerance": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 65,
          "description": "How far a colour may be from the key colour and still be taken out: higher takes out paler and duller shades of it too, a screen's creases and shadows, lower only colours as vivid as the key's. Colours far round the wheel from it, yellow or cyan for a green key, and greys never are. Unit: %."
        },
        "softness": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 20,
          "description": "How gradually the parts taken out fade to black at their edges, so hair and motion blur keep a soft edge rather than a hard cut. Unit: %."
        },
        "spill": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 50,
          "description": "How much of the key colour is taken out of what stays, the green or blue a screen casts on hair and edges, its light with it, as a keyer's despill does: 0 leaves it, 100 takes out all of it, so a soft edge goes dark rather than green. Only colours near the key colour change. Unit: %."
        },
        "matte": {
          "type": "integer",
          "enum": [
            0,
            1
          ],
          "minimum": 0,
          "maximum": 1,
          "default": 0,
          "description": "0 the picture, keyed; 1 the matte: white where the picture stays, black where it is taken out and grey at soft edges, to check the key, or to export as a mask for another editor."
        }
      }
    },
    "customShader": {
      "type": "object",
      "additionalProperties": false,
      "description": "A GLSL ES 3.00 fragment shader of your own, in Shadertoy's form (void mainImage(out vec4 fragColor, in vec2 fragCoord), reading the picture as iChannel0), drawn as a pass of its own at its place in the order. The code itself is set separately (window.videoEffects.loadShader, the WebMCP tool video_effects_load_shader, or the studio's shader editor) and is never part of the settings or a link: only its strength is.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom shader's result is applied, mixed on the sRGB-encoded values it reads and writes: 100 is the shader's output as written, 50 halfway between the picture without it and with it, 0 none. It changes nothing while no shader is set, and presets leave it, and the shader, as they are. Unit: %."
        }
      }
    },
    "customShader2": {
      "type": "object",
      "additionalProperties": false,
      "description": "A GLSL ES 3.00 fragment shader of your own, in Shadertoy's form (void mainImage(out vec4 fragColor, in vec2 fragCoord), reading the picture as iChannel0), drawn as a pass of its own at its place in the order. The code itself is set separately (window.videoEffects.loadShader, the WebMCP tool video_effects_load_shader, or the studio's shader editor) and is never part of the settings or a link: only its strength is. This is the look's Custom shader 2.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom shader's result is applied, mixed on the sRGB-encoded values it reads and writes: 100 is the shader's output as written, 50 halfway between the picture without it and with it, 0 none. It changes nothing while no shader is set, and presets leave it, and the shader, as they are. Unit: %."
        }
      }
    },
    "customShader3": {
      "type": "object",
      "additionalProperties": false,
      "description": "A GLSL ES 3.00 fragment shader of your own, in Shadertoy's form (void mainImage(out vec4 fragColor, in vec2 fragCoord), reading the picture as iChannel0), drawn as a pass of its own at its place in the order. The code itself is set separately (window.videoEffects.loadShader, the WebMCP tool video_effects_load_shader, or the studio's shader editor) and is never part of the settings or a link: only its strength is. This is the look's Custom shader 3.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom shader's result is applied, mixed on the sRGB-encoded values it reads and writes: 100 is the shader's output as written, 50 halfway between the picture without it and with it, 0 none. It changes nothing while no shader is set, and presets leave it, and the shader, as they are. Unit: %."
        }
      }
    },
    "customShader4": {
      "type": "object",
      "additionalProperties": false,
      "description": "A GLSL ES 3.00 fragment shader of your own, in Shadertoy's form (void mainImage(out vec4 fragColor, in vec2 fragCoord), reading the picture as iChannel0), drawn as a pass of its own at its place in the order. The code itself is set separately (window.videoEffects.loadShader, the WebMCP tool video_effects_load_shader, or the studio's shader editor) and is never part of the settings or a link: only its strength is. This is the look's Custom shader 4.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom shader's result is applied, mixed on the sRGB-encoded values it reads and writes: 100 is the shader's output as written, 50 halfway between the picture without it and with it, 0 none. It changes nothing while no shader is set, and presets leave it, and the shader, as they are. Unit: %."
        }
      }
    },
    "customComponent": {
      "type": "object",
      "additionalProperties": false,
      "description": "A React component of your own (a default export, written in JSX or TSX), rendered for each frame in a sandbox with no network and drawn over the picture at its place in the order. The code itself is set separately (window.videoEffects.loadComponent, the WebMCP tool video_effects_load_component, or the studio's component editor) and is never part of the settings or a link: only its strength is.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom component's drawing shows over the picture, laid over it on the sRGB-encoded values as a browser lays one element over another: 100 is the drawing as written, 50 at half its opacity, 0 none. It changes nothing while no component is set, and presets leave it, and the component, as they are. Unit: %."
        }
      }
    },
    "customComponent2": {
      "type": "object",
      "additionalProperties": false,
      "description": "A React component of your own (a default export, written in JSX or TSX), rendered for each frame in a sandbox with no network and drawn over the picture at its place in the order. The code itself is set separately (window.videoEffects.loadComponent, the WebMCP tool video_effects_load_component, or the studio's component editor) and is never part of the settings or a link: only its strength is. This is the look's Custom component 2.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom component's drawing shows over the picture, laid over it on the sRGB-encoded values as a browser lays one element over another: 100 is the drawing as written, 50 at half its opacity, 0 none. It changes nothing while no component is set, and presets leave it, and the component, as they are. Unit: %."
        }
      }
    },
    "customComponent3": {
      "type": "object",
      "additionalProperties": false,
      "description": "A React component of your own (a default export, written in JSX or TSX), rendered for each frame in a sandbox with no network and drawn over the picture at its place in the order. The code itself is set separately (window.videoEffects.loadComponent, the WebMCP tool video_effects_load_component, or the studio's component editor) and is never part of the settings or a link: only its strength is. This is the look's Custom component 3.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom component's drawing shows over the picture, laid over it on the sRGB-encoded values as a browser lays one element over another: 100 is the drawing as written, 50 at half its opacity, 0 none. It changes nothing while no component is set, and presets leave it, and the component, as they are. Unit: %."
        }
      }
    },
    "customComponent4": {
      "type": "object",
      "additionalProperties": false,
      "description": "A React component of your own (a default export, written in JSX or TSX), rendered for each frame in a sandbox with no network and drawn over the picture at its place in the order. The code itself is set separately (window.videoEffects.loadComponent, the WebMCP tool video_effects_load_component, or the studio's component editor) and is never part of the settings or a link: only its strength is. This is the look's Custom component 4.",
      "properties": {
        "amount": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "default": 100,
          "description": "How much of the custom component's drawing shows over the picture, laid over it on the sRGB-encoded values as a browser lays one element over another: 100 is the drawing as written, 50 at half its opacity, 0 none. It changes nothing while no component is set, and presets leave it, and the component, as they are. Unit: %."
        }
      }
    },
    "clips": {
      "type": "array",
      "description": "A timeline's looks, clip by clip in its order, as the studio's Export JSON writes them for two clips or more: applied to the studio, each clip takes the look at its place (read over its own), and clips past the list keep theirs; without a timeline the top-level look applies, the one the file was written from. The command line reads the top-level look only.",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "settings"
        ],
        "properties": {
          "settings": {
            "$ref": "#",
            "description": "The clip's look, a settings object."
          }
        }
      }
    },
    "order": {
      "type": "array",
      "items": {
        "enum": [
          "transform",
          "lensDistortion",
          "rgbSplit",
          "chromaticAberration",
          "blur",
          "sharpen",
          "rain",
          "pixelate",
          "pattern",
          "ascii",
          "glitch",
          "chromaKey",
          "light",
          "bloom",
          "halation",
          "vignette",
          "fade",
          "blackWhite",
          "colourWheels",
          "curves",
          "lut",
          "lut2",
          "lut3",
          "lut4",
          "customShader",
          "customShader2",
          "customShader3",
          "customShader4",
          "scanlines",
          "grain",
          "dither",
          "letterbox",
          "opacity",
          "customComponent",
          "customComponent2",
          "customComponent3",
          "customComponent4"
        ]
      },
      "uniqueItems": true,
      "default": [
        "transform",
        "lensDistortion",
        "rgbSplit",
        "chromaticAberration",
        "blur",
        "sharpen",
        "rain",
        "pixelate",
        "pattern",
        "ascii",
        "glitch",
        "chromaKey",
        "light",
        "bloom",
        "halation",
        "vignette",
        "fade",
        "blackWhite",
        "colourWheels",
        "curves",
        "lut",
        "lut2",
        "lut3",
        "lut4",
        "customShader",
        "customShader2",
        "customShader3",
        "customShader4",
        "scanlines",
        "grain",
        "dither",
        "letterbox",
        "opacity",
        "customComponent",
        "customComponent2",
        "customComponent3",
        "customComponent4"
      ],
      "description": "The effects' processing order, first applied first (the studio's cards, top to bottom), by effect id. The effects a partial list names take, in its order, the places they have in the default order (this default), and the others go to their default places: a partial list is read against the default order, not the look's current one. Effects next to each other that change where the picture is read or filter it (transform, then lensDistortion, rgbSplit, chromaticAberration, blur, sharpen, then rain, pixelate, pattern, ascii, glitch, chromaKey) work together in one read of it while they come in that order (the lens, channel, blur and sharpening effects in any order among themselves, but sharpen before blur still sharpening first); bloom and halation next to each other glow from the same light. Presets and reset put the default order back."
    }
  },
  "examples": [
    {
      "version": 1,
      "preset": "warm-film",
      "exposure": 0.2,
      "grain": {
        "amount": 30,
        "size": 1.2,
        "seed": 7
      }
    }
  ]
}
