The .tifo format v2

A public, JSON-based file format for stadium choreographies. Author it by hand, from code, or from an LLM — then validate before import.

Why this exists

A .tifo file describes a complete stadium tifo: the stadium geometry, the per-seat colors, floating text and image objects, and metadata. Because it's plain JSON with a published schema and a validation endpoint, any system — including language models like Claude or GPT — can generate a tifo from a prompt and reference image, validate it, and hand a user a file that imports into TifoMaker with full fidelity.

The shape

{
  "format": "tifo",
  "schemaVersion": 2,
  "meta": {
    "title": "Messi — THE GOAT",
    "generator": "claude-opus-4.8",
    "description": "North-stand Messi mosaic"
  },
  "stadium": {
    "templateId": "generic-bowl-60k",
    "templateVersion": 1
  },
  "palette": ["#262a33", "#5b2a86", "#d9f000"],
  "layers": [
    {
      "id": "base",
      "kind": "cells",
      "cellsRle": [[1, 30416], [2, 30416]]
    }
  ],
  "objects": [
    {
      "id": "t1", "kind": "text", "text": "THE GOAT",
      "fontId": "block", "colorIndex": 2,
      "cx": 2000, "cy": 300, "width": 1400, "height": 220
    }
  ]
}

Fields

FieldRequiredNotes
formatyesMust be "tifo" (legacy "tifo-v1" is accepted and migrated).
schemaVersionyesMust be 2.
stadiumyes{ templateId, templateVersion } — must name a known stadium.
paletteyes2–8 hex colors (#rgb or #rrggbb). Index 0 is the empty seat.
layersyes≥1 cells layer. See cell encoding below.
objectsnoFloating text / image objects rendered over the cells.
metanoTitle, author, generator, description — all optional.

Cell encoding (run-length)

Each cells layer stores cellsRle: an array of [paletteIndex, count] pairs, in seat order. The counts must sum to exactly the stadium's seat count. RLE keeps files tiny — a mostly-uniform design compresses thousands-fold — and is easy to generate from a described pattern.

Seat order is the stadium's canonical generation order. To produce art from an image, the reliable path is to build the design in the editor or compute per-seat colors against the stadium geometry, then RLE-encode. Counts that don't sum to the seat count are rejected.

Stadiums

templateIdSeats
generic-bowl-60k60,832
single-kop-40k39,700
grand-oval-76k75,984

Validate before you ship key

POST a document to the validation endpoint to check it without saving. This is the loop that gives generators 100% reliability: write → validate → fix the reported paths → repeat.

POST /api/tifo/validate
Content-Type: application/json

{ ...your .tifo document... }

→ 200 OK
{
  "schemaVersion": 2,
  "valid": false,
  "errors": [
    { "path": "palette[1]", "message": "\"blue\" is not a #rrggbb hex color" },
    { "path": "layers[0].cellsRle", "message": "runs sum to 500 seats but stadium has 60832" }
  ]
}

Every error carries a path (the exact offending field) and a human-readable message. A valid: true response means the document will import cleanly. Note that a 200 response with valid: false is normal — an invalid document is still a successful validation.

Importing

Users open a .tifo file in the editor via Open file. If the file targets a different stadium than the one open, the editor switches to the matching stadium automatically. Import runs the same validation as the endpoint, so anything that validates will open.

Security & compatibility

← Open the editor