A public, JSON-based file format for stadium choreographies. Author it by hand, from code, or from an LLM — then validate before import.
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.
{
"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
}
]
}
| Field | Required | Notes |
|---|---|---|
format | yes | Must be "tifo" (legacy "tifo-v1" is accepted and migrated). |
schemaVersion | yes | Must be 2. |
stadium | yes | { templateId, templateVersion } — must name a known stadium. |
palette | yes | 2–8 hex colors (#rgb or #rrggbb). Index 0 is the empty seat. |
layers | yes | ≥1 cells layer. See cell encoding below. |
objects | no | Floating text / image objects rendered over the cells. |
meta | no | Title, author, generator, description — all optional. |
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.
| templateId | Seats |
|---|---|
generic-bowl-60k | 60,832 |
single-kop-40k | 39,700 |
grand-oval-76k | 75,984 |
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.
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.
tifo-v1 files (flat cells) are accepted and migrated to v2 on import — old files never break.