← TRAMOYATRAMOYA — THE FORMAT

SONG PERFORMANCE JSON

the file they both speak

PLAIN JSONCC BY 4.0NO ACCOUNT

THERE IS NO NEW FORMAT HERE

Song Performance JSON is the name for a file I had already been writing for two years. One song, one plain text file: the words, their translations, the tempo, and when each line lands. It is not a second format beside the one my songs were already in — it is that one, finally given a name.

Nothing here needs an account, a server or a database. You can open the file in any text editor and read every value in it.

A COMPLETE EXAMPLE

This is Pimiento, whole and unedited: nineteen sung lines, nineteen timings, four languages, and a tempo that was measured rather than guessed. Nothing is trimmed and nothing is invented — the page renders the same file you can download below.

{
  "title": "Pimiento",
  "artist": "Chango Pepper",
  "notes": "Capo 3, Acordes de DO",
  "title_translations": {
    "es": "Pimiento",
    "en": "Pepper Tree",
    "fr": "Pimentier",
    "nl": "Peperboom"
  },
  "tempo": {
    "bpm": 66.67,
    "numerator": 6,
    "denominator": 8,
    "countInBars": 1
  },
  "intro": {
    "es": "Las cosas esenciales que nos sostienen en la vida.",
    "en": "The essential things that carry us through life.",
    "fr": "Les choses essentielles qui nous portent dans la vie.",
    "nl": "De essentiële dingen die ons door het leven dragen."
  },
  "lyrics": [
    {
      "es": "Viejo pimiento,\nhoy vuelvo a visitarte",
      "en": "Old pepper tree,\ntoday I come back to see you,",
      "fr": "Vieux pimentier,\naujourd'hui je reviens te voir,",
      "nl": "Oude peperboom,\nvandaag kom ik je weer bezoeken,"
    },
    {
      "es": "Dicen que sabes tratar\nmales de poetas.",
      "en": "They say you know how to heal\na poet's wounds.",
      "fr": "On dit que tu sais soigner\nles peines des poètes.",
      "nl": "Men zegt dat jij weet hoe je\ndichterswonden heelt."
    },
    {
      "es": "Con el corazón afuera nací,",
      "en": "I was born with my heart outside,",
      "fr": "Je suis né le cœur à l'air,",
      "nl": "Ik werd geboren met mijn hart naar buiten,"
    },
    {
      "es": "desde niño quiere más que latir.",
      "en": "since childhood it wanted more than to beat.",
      "fr": "depuis l'enfance il veut plus que battre.",
      "nl": "al van jongs af wil het meer dan kloppen."
    },
    {
      "es": "No quiere que yo le cuente del mundo,",
      "en": "It won't hear my stories of the world,",
      "fr": "Il ne veut pas que je lui raconte le monde,",
      "nl": "Het wil niet dat ik het de wereld vertel,"
    },
    {
      "es": "por sí mismo él lo quiere sentir.",
      "en": "it wants to feel it by itself.",
      "fr": "il veut le sentir lui-même.",
      "nl": "het wil het zelf voelen."
    },
    {
      "es": "Viejo pimiento, mi refugio y mi lugar.",
      "en": "Old pepper tree, my refuge, my place.",
      "fr": "Vieux pimentier, mon refuge, mon lieu.",
      "nl": "Oude peperboom, mijn toevlucht, mijn plek."
    },
    {
      "es": "Desnudo el corazón,",
      "en": "The heart laid bare,",
      "fr": "Le cœur à nu,",
      "nl": "Het hart ontbloot,"
    },
    {
      "es": "todo lo atraviesa:",
      "en": "everything passes through it:",
      "fr": "tout le traverse :",
      "nl": "alles gaat erdoorheen:"
    },
    {
      "es": "Farol en tormentas,\npero se quiebra con un mirar.",
      "en": "A lantern in storms,\nyet a single glance can break it.",
      "fr": "Lanterne dans la tempête,\nmais un regard suffit à la briser.",
      "nl": "Een lantaarn in stormen,\nmaar één blik kan het breken."
    },
    {
      "es": "Ama rápido,",
      "en": "It loves quickly,",
      "fr": "Il aime vite,",
      "nl": "Het houdt snel lief,"
    },
    {
      "es": "sin medida va.",
      "en": "without measure it goes.",
      "fr": "sans mesure il va.",
      "nl": "zonder maat gaat het."
    },
    {
      "es": "Encuentra belleza en lo sencillo.",
      "en": "It finds beauty in simple things.",
      "fr": "Il trouve la beauté dans le simple.",
      "nl": "Het vindt schoonheid in het eenvoudige."
    },
    {
      "es": "Pero en su sangre carga con penas ajenas,",
      "en": "Yet in its blood it carries others' sorrows,",
      "fr": "Mais dans son sang il porte les peines des autres,",
      "nl": "Maar in zijn bloed draagt het andermans verdriet,"
    },
    {
      "es": "y se funde.",
      "en": "and dissolves in them.",
      "fr": "et s'y dissout.",
      "nl": "en lost daarin op."
    },
    {
      "es": "Si a quedarse afuera insiste,",
      "en": "If it insists on staying outside,",
      "fr": "S'il insiste pour rester dehors,",
      "nl": "Als het buiten wil blijven,"
    },
    {
      "es": "En tu sombra déjalo latir.",
      "en": "let it beat in your shade.",
      "fr": "laisse-le battre dans ton ombre.",
      "nl": "laat het kloppen in jouw schaduw."
    },
    {
      "es": "Viejo pimiento,\nfuiste el juego en mi infancia.",
      "en": "Old pepper tree,\nyou were the playground of my childhood.",
      "fr": "Vieux pimentier,\ntu fus le jeu de mon enfance.",
      "nl": "Oude peperboom,\nje was het spel van mijn jeugd."
    },
    {
      "es": "Me cubro con tus hojas pa volver a nacer.",
      "en": "I wrap myself in your leaves to be born again.",
      "fr": "Je me couvre de tes feuilles pour renaître.",
      "nl": "Ik hul me in je bladeren om opnieuw geboren te worden."
    }
  ],
  "timelineVersion": 2,
  "leadIn": {
    "durationSec": 8.92,
    "source": "measured",
    "confidence": "low",
    "apply": false
  },
  "timeline": [
    {
      "start": 0.0,
      "end": 9.52
    },
    {
      "start": 9.52,
      "end": 20.54
    },
    {
      "start": 20.54,
      "end": 28.62
    },
    {
      "start": 28.62,
      "end": 34.42
    },
    {
      "start": 34.42,
      "end": 40.98
    },
    {
      "start": 40.98,
      "end": 50.08
    },
    {
      "start": 50.08,
      "end": 58.56
    },
    {
      "start": 58.56,
      "end": 65.42
    },
    {
      "start": 65.42,
      "end": 69.54
    },
    {
      "start": 69.54,
      "end": 80.22
    },
    {
      "start": 80.22,
      "end": 83.74
    },
    {
      "start": 83.74,
      "end": 87.62
    },
    {
      "start": 87.62,
      "end": 95.84
    },
    {
      "start": 95.84,
      "end": 106.0
    },
    {
      "start": 106.0,
      "end": 122.1
    },
    {
      "start": 122.1,
      "end": 126.9
    },
    {
      "start": 126.9,
      "end": 135.5
    },
    {
      "start": 135.5,
      "end": 145.14
    },
    {
      "start": 145.14,
      "end": 165.22
    }
  ]
}
DOWNLOAD THIS EXAMPLE →

This is a snapshot of one real song file, taken on 16 August 2026. It is a copy kept here to illustrate the format, not a live mirror — the working file goes on changing, and this one stays as it was.

THE KEYS

In the order they appear in the file above. Key order carries no meaning in JSON and you should not infer any from it — a file that lists these keys in a different order is exactly as valid, and Bombista preserves whatever order it is given.

keytypewhat it holds
titlestringThe song's title, in the language it is sung in.
artiststringWho performs it.
notesstringAnything for the performer — tuning, capo, chord shapes. Never projected.
title_translationsobjectThe title in each language, including the one it was written in.
tempoobjectbpm, numerator, denominator, countInBars. Omitted entirely when the number is not real — see below.
introobjectA spoken introduction, translated like a line. Not part of lyrics, so it has no timing.
lyricsarray of objectsSung lines only, in order. Each line is an object of languages — not a string — so no translation is lost on a round trip.
timelineVersionnumber2. Anything else is rejected rather than coerced.
leadInobjectdurationSec, source, confidence, apply. The audio before the first sung word.
timelinearray of objects{ start, end } in seconds. Entry i belongs to lyrics[i]; entry 0 always starts at 0.00.

WHICH KEYS BOMBISTA WRITES

Exactly five. Everything else passes through byte-for-byte.

linesHashA fingerprint of the words, so a timeline can tell you it no longer matches the lyrics it was made for.
timelineSignedOffA timestamp, written when a human downloads the file — the difference between a machine timeline and a reviewed one.
timelineVersionAlways 2.
leadInMeasured, never guessed.
timelineThe timings themselves.

title, artist, notes, title_translations, tempo, intro and lyrics are read and never written. Bombista does not translate, does not retitle, and does not compute a tempo.

Bombista receives a file and returns a file. It does not change the state of one.

The file shown here does not carry linesHash or timelineSignedOff, and that is a fact about this snapshot rather than about the format. Both are written when a timeline is reviewed and signed off, so a file that has been through that step carries them and this one simply had not.

THE PART THE TWO TOOLS AGREE ON

Three of those keys — timelineVersion, leadIn and timeline — are a strict sub-envelope inside the larger file: the exact and only thing Bombista promises to produce and Pregonero promises to consume, specified separately so neither tool can drift from the other.

One format with a contract inside it, not three formats.

{
  "timelineVersion": 2,
  "leadIn": { "durationSec": 7.26, "source": "measured", "confidence": "low", "apply": false },
  "timeline": [ { "start": 0.00, "end": 5.84 } ]
}
THE FULL CONTRACT ON GITHUB →

ABSENT BEATS NULL

When a song's tempo is not known, the tempo block is left out of the file. It is not set to null, and it is not filled with a placeholder.

This is the most useful thing on this page for anyone designing a format of their own. A placeholder tempo has two consumers that need opposite things from it: it is the denominator that scales a performance to its recording, and it also drives a visual pulse. A number chosen to keep the pulse looking sane is the wrong denominator for scaling, and a number chosen to make scaling behave is the wrong pulse. There is no single value that is right for both, so the block can only be right when the number is real.

null is not neutral once a consumer reads it. Absent is the honest state.

The consequence, plainly: with no tempo block there is no visual pulse and no count-in, and performed-tempo scaling is pinned to 1. Pregonero is already built to degrade that way, which is why leaving the key out is safe and filling it in is not.

LICENCE

Anyone may read or write this format. No permission is needed and none is implied — a file format is not the kind of thing that can be owned.

The text of this page — the specification itself — is published under CC BY 4.0, so it may be republished, translated or built on with attribution.

Any reference code, such as a validator or a schema, is MIT, matching both tools.

CC BY 4.0 →