{
  "openapi": "3.1.1",
  "info": {
    "title": "Retro Dither \u0026 Machine Palette Converter API",
    "version": "1.0.0",
    "summary": "Dither images to 1-bit, CGA, EGA, C64-, NES- or handheld-style palettes.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://fliplineditherlab.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Dither images to 1-bit, CGA, EGA, C64-, NES- or handheld-style palettes.",
        "description": "Resample a photo to a machine-style resolution, then quantise it with ordered (Bayer, blue noise) or error-diffusion dithering (Floyd–Steinberg, Atkinson, Jarvis, Stucki, Burkes, Sierra). Optional attribute cells imitate C64 and NES colour limits. Download a crisp PNG and a reusable recipe.\n\nMethodology:\n- Decode, auto-orient and convert to sRGB (4 MB, 2 MP). Area-resample to the working grid in linear light; machine resolutions crop, pad (black) or stretch to their logical size. Transparent pixels are composited over black.\n- Brightness, contrast, gamma and saturation use the Retro Lab development step. Palettes: CGA/EGA from RGBI and rgbRGB signal bits, the NES-style set decoded from the 2C02 composite levels on NESdev, C64 from Pepto’s VIC-II measurements; handheld greens are an authored interpretation.\n- Ordered dithering adds a threshold bias (Bayer 2/4/8, or a 32 × 32 void-and-cluster map) scaled by the palette’s mean nearest-colour spacing. Error diffusion uses the published kernels, optionally serpentine; Atkinson drops a quarter of the error. Matching uses sRGB, redmean-weighted RGB or linear-light distance.\n- Attribute cells pick colours per cell greedily (largest error reduction first); NES and C64 multicolour share the most frequent colour as background. Wide-pixel modes double pixels horizontally. Integer output scaling repeats pixels.\n\nMachine palettes are colour values only. The cell modes approximate colour limits; they do not produce files for real hardware.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_dither_lab",
        "x-owner-app": "flipline",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineDitherLabInput"
              },
              "examples": {
                "example1": {
                  "summary": "Handheld LCD greens with a 4 × 4 Bayer pattern",
                  "value": {
                    "image": {
                      "filename": "test-card.png",
                      "contentType": "image/png",
                      "contentBase64": "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"
                    },
                    "resolution": "gb",
                    "palette": "gb-green",
                    "dither": "bayer4",
                    "scale": 2
                  }
                },
                "example2": {
                  "summary": "C64-style multicolour cells",
                  "value": {
                    "image": {
                      "filename": "test-card.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAYAAAB1ovlvAAAIm0lEQVR4AWJ0qzjxn2EUjIIBAiyMjAPviFEwcgEwAY6mwFEwcIBlNPmNgoEEo1XwKBhQMFoFj4IBBaMl4CgYUDDaBhwFAwpGq+BRMKBgtAoeBQMKRhPgKBhQAGwDjqbAUTBwYLQEHAUDCkYT4CgYUDDaCx4FAwpGxwFHwYCC0Sp4FAwoGK2CR8GAgtEScBQMKBhtA46CAQWjVfAoGFAwWgUPIbDEphtDLOZI6QC4hHqAMabz/OiuuEEKFlsDExyogIAVEuiFBXLMgdhAHHt0aCVIxtjOC6MJcJCBRTZd2BMettoKFnvQBAjDcUfLaOtIKgHGuK7RBDhYwEIrYMJjAjJgiQ9f6QcDOBIgCMcPgUTIGN89mgAHA1gASnygREZMAgSxsVS/WPE/BoaE44M3ITIm9FwcTYADDOZbdqImPGJLQTylH3ICBOHEE+W09ALZgAndn6OYvhgl8YFoUjAzGhuZj2bmfIvOQen/0WGYAQRzgYkCI/HBYgZXKYgOkEtBUGkH0ofMhtEgIwZhXDMm910arYIHAMwx68BZWuFNgDAaOeHBaFBiQ6t6UdhAnHKygnaeIgOMloADBdATF7bEh54IYfpgAL0NCGMjlXpgNpIdc8w7GFJPDZ5EODoXPABglimw9MPVKCKmFIQBXNUvCKAlPGSxwRTno3PBAwGIaZ3jKwWRAbbSDznRgQByYgRiUAZIP1NJXT+RCUar4IEAuEo9bB0RXKUgttIPV9WLnnhBpeAgiffRBEhnMN24HW/CIKpHDAPopR8IYEt4yO1HKB4s8Q4cBxyF9IQogEAiwdpDxlZaYislcSVuqNh0o/ZBER6jJeBAAAKJA0MMW1WMXPoRqG5xiQ2GuB9NgPQG6OFNakJEToDEJDw8dg+SBDiaAgcM4At6fO1DEI2c+PCZiceOwRD3o+OAgxWgz0/RYL5qMMT9aBU8kIBQIsPGB5V8MDZyOxBZDJ8ZSGC0Ch6JgJhEhj68gpzwkMcB0TEuM5DFkPiDowoeTX/0B/gSDHrvFgZgA83YEiDyIgQSEuLgKAEH3g0jC5CwkhllaRUIII8V4lh4irMERUuIRVdqBksbcDQJ0hMUX61l6NVtxp/4sCVE5NIPBvCsgMabCAdJ9QsCo1XwQABiEx4IIJd6yDQ2c7CtB0QvFeEJkAb+IgOMJsCBAKSUeuilH74EiK09iAWXXq8dnQseybDseh3h6hO5BAPhv0g0DKOrQdeLIwEOqtAom3MDuVk8CugIOrWa8C/HR556AwFspRa+UhBLoiwHJf5BBEar4IEEsHYeto1E2Np+2DohMJpQexBIV9yoG3Qbk0Z7wQMIKm/VM7SrN2LvcOBaWIA8DggDhEpBKD0Y43p0X/AA46qb9dirTeR2H7oYsjihtiCUrgIm9kEZBlXzb462AQcBaFVrxB9TIACjkQGBargamPAGM2CsXnBrNAEOEtCi0kBaAkROfDAaCdfcBpo3yAFjzcLRBDjYQLNyA/YECAMgPnobEEYDce0doP4hAhhrRxPgoAVNsIQIAtiqXxiAJry6u0D1Qwww1i26PZoAR8GAgdFxwFEwoGA0AY6CAQWj9wWPggEFoyXgKBhQMJoAR8GAgtG54FEwoGB0T8goGFAwWgWPggEFo1XwKBhQMFoC0gEcvJ2JNKOGCHDkoIexrVWn0ctZgwKMtgFpBPbfQiQ6UsCxW9lg2lptKnUdNEjBaBVMRbD3ZjpKCUcJOApMiCCTrNWHd4k4WgVTAHZfT4ezaRWMR29kgWkbzeGZEEcTIAlgx7U0nG04WoOj17MYbLSGXyIcnQtGA1uvpsLZ2DoJAwmOXM0CZwAb7eGREK9fLRh+JeC6K8lwNrYERKgXOhQyJCgh2uoM7UR49UoBOKSHfAJcfTkJTA+lBEQNcPhKFoOd7tBLhFcuF6Dwh2wveOXFxBHfeDh8CZgI9acPvEOIBJcu5mEUEIwajTb/M/WXDpCTSAcrgAkPBiitYoeLvP0QSIQXgYkPBNDdz6jRYAPeE5JlOLgT4bLzCWB6JCYwYuQdDAdvIjx/HlTyQQDOBAjiZBkto7/rCIAl5+LB9EhPYMTIOxjNQBIZHODcuVwwjcv9yHcqMkw7FwU+vGawYFjiGwXEgQPnMgZV/MESHz7AqFGPKAEhNCNDtsnAVscLz6CWesjs0RKQsLyjycBXx6fP5BLlfpQSEAamnokGn6Q0EBiW+EYB+WD/GeBCiAGKPxAGJT5iAaNGHWYJCAO55vRrE847GYth/2gJR5m8szn924QnT+bA2WSXgDAw5SR92oRzoYlvFFAX7DtJ3zbhiZOQpWSkAEaNWtwlIIyVZ7mc0rDACmYfj8Gwa7QEo428i+VMJB71wbHjkFU7pLqPqAQIY+dZUS8hzjwWM5pA6CzvYkWbRHjkGGiRBHnuIykBgkC+NWWJcMZRzFKPFPtH5SmTd7WmXiI8fBRS6lHiPkaNGtISIEw+35a0hDjtcBRZDhyVp768qy3lifDgYdQtB3RPgMhiuBLjFGiio9T8UXnayLuRkRAPABMeNd3HqFFNeQIclR/a8m72hBPivoMZNLGfhaDNo2DYg10HQZupIIBQAqI2wDsOOApGAa0BC8ph16NgFNAZjJaAo2BAwWgJOAoGFIyWgKNgQMFoCTgKBhSMloCjYEDBaAIcBQMKRqvgUTCggPHZs2fUTYJvnKhqnNO3VVQ1j+EbYgUHNcCCEuC1+1QEXAzUdV8Y1wKqmvfmhg9VzRutgkfBgILRBDgKBhQM3gQI2CgYEWA0AY6CAQWjCXAUDCgYTYCjYEDBaAIcBQMKRhPgKBhQMJoAR8GAgtEEOAoGFID2mvxnYmICn2qETFMiRg0zRu0fIfYPeBYYBSMajCbAUTCgYDQBjoIBBaMJcBQMKBhNgKNgQMFoAhwFAwpGE+AoGFAwmgBHwYCC0QQ4CgYUAAB1iIOrsOcKkwAAAABJRU5ErkJggg=="
                    },
                    "resolution": "c64-multicolor",
                    "palette": "c64",
                    "cells": "c64-multicolor",
                    "dither": "bayer4",
                    "scale": 2
                  }
                },
                "example3": {
                  "summary": "1-bit Atkinson at 160 px",
                  "value": {
                    "image": {
                      "filename": "test-card.png",
                      "contentType": "image/png",
                      "contentBase64": "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"
                    },
                    "width": 160,
                    "palette": "mono",
                    "dither": "atkinson",
                    "scale": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineDitherLabResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FliplineDitherLabInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "image": {
            "title": "Image (PNG, JPEG or WebP)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 3906 KiB. Accepts .png,.jpg,.jpeg,.webp.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "resolution": {
            "title": "Machine resolution",
            "type": "string",
            "enum": [
              "custom",
              "gb",
              "cga",
              "ega",
              "c64-hires",
              "c64-multicolor",
              "nes",
              "mac"
            ],
            "default": "custom"
          },
          "width": {
            "title": "Working width (px)",
            "type": "integer",
            "minimum": 16,
            "maximum": 960,
            "default": 320
          },
          "fit": {
            "title": "Image fit for machine resolutions",
            "type": "string",
            "enum": [
              "cover",
              "contain",
              "stretch"
            ],
            "default": "cover"
          },
          "scale": {
            "title": "Output scale (whole pixels)",
            "type": "integer",
            "minimum": 1,
            "maximum": 6,
            "default": 2
          },
          "palette": {
            "title": "Retro palette",
            "type": "string",
            "enum": [
              "none",
              "mono",
              "gb-green",
              "gb-gray",
              "cga-0-low",
              "cga-0-high",
              "cga-1-low",
              "cga-1-high",
              "cga-5-high",
              "cga-16",
              "ega-64",
              "c64",
              "nes",
              "rgb-8",
              "custom"
            ],
            "default": "gb-green"
          },
          "customPalette": {
            "title": "Custom palette (HEX list, GPL or JASC-PAL)",
            "type": "string",
            "maxLength": 16384,
            "default": ""
          },
          "dither": {
            "title": "Dithering method",
            "type": "string",
            "enum": [
              "none",
              "bayer2",
              "bayer4",
              "bayer8",
              "blue-noise",
              "floyd",
              "atkinson",
              "jarvis",
              "stucki",
              "burkes",
              "sierra3",
              "sierra2",
              "sierra-lite"
            ],
            "default": "bayer4"
          },
          "strength": {
            "title": "Dither strength (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 100
          },
          "serpentine": {
            "title": "Serpentine scanning for error diffusion",
            "type": "boolean",
            "default": true
          },
          "match": {
            "title": "Colour matching",
            "type": "string",
            "enum": [
              "srgb",
              "redmean",
              "linear"
            ],
            "default": "srgb"
          },
          "cells": {
            "title": "Attribute cells",
            "type": "string",
            "enum": [
              "none",
              "c64-hires",
              "c64-multicolor",
              "nes"
            ],
            "default": "none"
          },
          "brightness": {
            "title": "Brightness",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "contrast": {
            "title": "Contrast",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "gamma": {
            "title": "Gamma",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 1
          },
          "saturation": {
            "title": "Saturation (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 200,
            "default": 100
          }
        },
        "required": [
          "image"
        ]
      },
      "FliplineDitherLabResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "colorsUsed",
              "usage",
              "background",
              "artifact",
              "recipe",
              "openInApp"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "colorsUsed": {
                "type": "integer"
              },
              "usage": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "color",
                    "pixels"
                  ],
                  "properties": {
                    "color": {
                      "type": "string"
                    },
                    "pixels": {
                      "type": "integer"
                    }
                  }
                }
              },
              "background": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "recipe": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "settings"
                ],
                "description": "A settings-only Retro FX recipe (no images). Pipeline: fit and area resampling in linear light, colour development, optional palette quantisation with dithering and attribute cells, integer scale, then one CRT, VHS, CMYK halftone or riso effect per frame. Seed and frame number make every frame reproducible.",
                "properties": {
                  "format": {
                    "const": "flipline-retro-fx"
                  },
                  "version": {
                    "const": 1
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "resolution",
                      "width",
                      "fit",
                      "scale",
                      "adjust",
                      "color",
                      "effect",
                      "crt",
                      "vhs",
                      "print",
                      "seed",
                      "frames",
                      "fps"
                    ],
                    "properties": {
                      "resolution": {
                        "enum": [
                          "custom",
                          "gb",
                          "cga",
                          "ega",
                          "c64-hires",
                          "c64-multicolor",
                          "nes",
                          "mac"
                        ]
                      },
                      "width": {
                        "type": "integer",
                        "minimum": 16,
                        "maximum": 960
                      },
                      "fit": {
                        "enum": [
                          "contain",
                          "cover",
                          "stretch"
                        ]
                      },
                      "scale": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 6
                      },
                      "adjust": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "brightness",
                          "contrast",
                          "gamma",
                          "saturation"
                        ],
                        "properties": {
                          "brightness": {
                            "type": "number",
                            "minimum": -100,
                            "maximum": 100
                          },
                          "contrast": {
                            "type": "number",
                            "minimum": -100,
                            "maximum": 100
                          },
                          "gamma": {
                            "type": "number",
                            "minimum": 0.2,
                            "maximum": 3
                          },
                          "saturation": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 200
                          }
                        }
                      },
                      "color": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "palette",
                          "colors",
                          "dither",
                          "strength",
                          "serpentine",
                          "match",
                          "cells"
                        ],
                        "properties": {
                          "palette": {
                            "enum": [
                              "none",
                              "custom",
                              "mono",
                              "gb-green",
                              "gb-gray",
                              "cga-0-low",
                              "cga-0-high",
                              "cga-1-low",
                              "cga-1-high",
                              "cga-5-high",
                              "cga-16",
                              "ega-64",
                              "c64",
                              "nes",
                              "rgb-8"
                            ]
                          },
                          "colors": {
                            "type": "array",
                            "maxItems": 256,
                            "items": {
                              "type": "string",
                              "pattern": "^#[a-fA-F0-9]{6}$"
                            }
                          },
                          "dither": {
                            "enum": [
                              "none",
                              "bayer2",
                              "bayer4",
                              "bayer8",
                              "blue-noise",
                              "floyd",
                              "atkinson",
                              "jarvis",
                              "stucki",
                              "burkes",
                              "sierra3",
                              "sierra2",
                              "sierra-lite"
                            ]
                          },
                          "strength": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "serpentine": {
                            "type": "boolean"
                          },
                          "match": {
                            "enum": [
                              "srgb",
                              "redmean",
                              "linear"
                            ]
                          },
                          "cells": {
                            "enum": [
                              "none",
                              "c64-hires",
                              "c64-multicolor",
                              "nes"
                            ]
                          }
                        }
                      },
                      "effect": {
                        "enum": [
                          "none",
                          "crt",
                          "vhs",
                          "halftone",
                          "riso"
                        ]
                      },
                      "crt": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "scanlines",
                          "mask",
                          "maskStrength",
                          "curvature",
                          "bloom",
                          "bloomRadius",
                          "vignette",
                          "noise",
                          "flicker"
                        ],
                        "properties": {
                          "scanlines": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "mask": {
                            "enum": [
                              "none",
                              "aperture",
                              "shadow",
                              "slot"
                            ]
                          },
                          "maskStrength": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "curvature": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "bloom": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "bloomRadius": {
                            "type": "number",
                            "minimum": 1,
                            "maximum": 16
                          },
                          "vignette": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "noise": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "flicker": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          }
                        }
                      },
                      "vhs": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "lumaBlur",
                          "sharpen",
                          "chromaBleed",
                          "chromaShift",
                          "saturation",
                          "tracking",
                          "trackingPosition",
                          "roll",
                          "headSwitch",
                          "jitter",
                          "noise"
                        ],
                        "properties": {
                          "lumaBlur": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "sharpen": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "chromaBleed": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "chromaShift": {
                            "type": "number",
                            "minimum": -12,
                            "maximum": 12
                          },
                          "saturation": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 200
                          },
                          "tracking": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "trackingPosition": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "roll": {
                            "type": "number",
                            "minimum": -25,
                            "maximum": 25
                          },
                          "headSwitch": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "jitter": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "noise": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          }
                        }
                      },
                      "print": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "pitch",
                          "shape",
                          "angles",
                          "gcr",
                          "paper",
                          "inks",
                          "screen",
                          "misregistration"
                        ],
                        "properties": {
                          "pitch": {
                            "type": "number",
                            "minimum": 3,
                            "maximum": 40
                          },
                          "shape": {
                            "enum": [
                              "round",
                              "line",
                              "diamond"
                            ]
                          },
                          "angles": {
                            "enum": [
                              "classic",
                              "single"
                            ]
                          },
                          "gcr": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 100
                          },
                          "paper": {
                            "type": "string",
                            "pattern": "^#[a-fA-F0-9]{6}$"
                          },
                          "inks": {
                            "type": "array",
                            "minItems": 1,
                            "maxItems": 3,
                            "items": {
                              "type": "string",
                              "pattern": "^#[a-fA-F0-9]{6}$"
                            }
                          },
                          "screen": {
                            "enum": [
                              "halftone",
                              "grain"
                            ]
                          },
                          "misregistration": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 8
                          }
                        }
                      },
                      "seed": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 2147483647
                      },
                      "frames": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 24
                      },
                      "fps": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 30
                      }
                    }
                  }
                }
              },
              "openInApp": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}