Spec reference
Version 0.20.3.
Validate with parseSwaralipi(json) (throws) or
safeParseSwaralipi(json).
Document shape
{
"version": "0.1.0",
"meta": { "title": "…", "taal": { "id": "dadra", "numBeats": 6 }, "tonic": "C" },
"parts": [
{ "name": "asthayi", "rows": [
{ "notes": [ /* one object per cell, left to right */ ] }
] }
]
} Document → parts[] → rows[] → notes[]. meta.title is
required; name/label/taal/tonic
are optional.
Optional meta.instrument sets the default melody instrument (else piano); the viewer ignores it.
Note kinds
Each note carries exactly one of these, plus optional flags:
| Field | Meaning |
|---|---|
svara: [octave, letter] | a pitch — letter ∈ s r g m p d n, octave ∈ ati-mandra mandra madhyam taar ati-taar |
sustain: true | আ-কার vowel hold (with svara = melisma) |
rest: true | বিরাম (silence) |
bar: "dari" | barline — dari (।) / single (I) / double (II) / quad (end) |
raw: "…" | verbatim source passthrough (lossless fallback) |
Akarmatrik extensions (flags)
| Flag | Meaning |
|---|---|
komal / tivra | flat / sharp |
microtone: "alpakomal" | "atikomal" | below komal |
durationMatra: 0.5 | fractional beat (½, ¼ …) |
halfMatra | ঃ prints after its note; ending only the first half keeps the cell open (-ঃমঃ / সঃ গরঃ = one matra, one column). Hand-set ঃ or aakar overrides raw marks in that cell. |
aakar?: boolean | false hides that note’s া; visual only, with duration and playback unchanged. Rests always show their sign. |
lyric: "অ" | per-syllable lyric (column-aligned) |
kan / resh | grace before / after — one [octave, letter], or a list of notes for a multi-swar grace |
meend: "start" | "end" | "both" | glide / slur endpoint; both ends one arc and starts the next |
brackets | repeat / () skip / [] alt-on-repeat |
above / below | recursive annotation notes; an above-note’s own above draws a second line (e.g. a taal mark over [ ] notes). Only rows using it grow; sam ticks and ‖ there affect playback too. |
review: "স্বর: পা?" | curation only — a converter's doubt about this cell; the creator outlines it until it is edited or accepted, viewer and player ignore it |
লয় — tempo tiers
A row may carry lay: "drut" | "madhya" | "vilambit", which holds
until another row changes it. meta.bpm is the দ্রুত (fast)
tempo; the other two play at a fraction of it — 0.75 and
0.5 by default. Override per document with
meta.laySpeeds, or per call with the player's / exporter's
laySpeeds option. A document with no lay anywhere
plays exactly as it did before the field existed.
{ "meta": { "bpm": 120, "laySpeeds": { "vilambit": 0.4 } },
"parts": [ { "rows": [
{ "lay": "vilambit", "notes": [ … ] }, // this row on, at 0.4 × 120
{ "notes": [ … ] }, // still বিলম্বিত
{ "lay": "drut", "notes": [ … ] } // back to 120
] } ] }
The player changes the tabla with it: tabla-js plays each stretch from single
bols at its own tempo, so the drum never drifts against the melody.
তালফেরতা — changing taal
A row may also carry taal (same shape as meta.taal)
and an optional printed taalName. It holds until another row
changes it; meta.taal is the opening taal. taal: null
switches to মুক্তছন্দ (the accompaniment rests). It is independent of
lay: a row may change either, both or neither.
{ "meta": { "taal": { "id": "dadra", "numBeats": 6, "bhaags": [3, 3] } },
"parts": [ { "rows": [
{ "notes": [ … ] }, // দাদরা
{ "taal": { "id": "shashthi", "numBeats": 6, "bhaags": [2, 4] },
"taalName": "ষষ্ঠী", "lay": "drut", "notes": [ … ] }, // ষষ্ঠী, দ্রুত
{ "taal": { "id": "kehrwa", "numBeats": 8, "bhaags": [4, 4] },
"notes": [ … ] } // কাহারবা, still দ্রুত
] } ] }
The viewer prints the change as a centred line between systems, as
স্বরবিতান does; the player swaps the theka (or click, drum pattern)
on that row, and the staff view changes its time signature there.
Tabla
All optional, all player-only. taal.variation picks a tabla-js
variation by name (any case); omitted, the taal plays its default. A taal may
also carry its own tabla — a theka of one cell per matra, with
fillerLight, fillerBusy, tihai +
tihaiArrival and bols — in tabla-js's
swaralipi-taal shape; every section must be whole cycles. A custom taal is
any id not in the catalogue, with its own cycle; without a
theka it plays a catalogue taal of the same vibhags and khaali, else a plain
theka on its vibhags. Its bhaags, sam and
khaali count from where its lines open: a sam on
another vibhag means each line opens before sam. Its theka is written from sam.
meta.fillers is auto (default), light,
busy or off; meta.tihai: true closes
the song with a tihai on its last সম.
{ "meta": { "taal": { "id": "kehrwa", "numBeats": 8, "variation": "Kaharwa 2" },
"fillers": "light", "tihai": true } }
{ "meta": { "taal": { "id": "mine", "numBeats": 4, "bhaags": [2, 2],
"theka": ["dha ge", "na", "ta ke", "na"], "tihaiArrival": "dha" } } }
Compressed rows
Every row shares one set of columns, so a row with extra notes widens every
line. "compress": true on that row squeezes it into the line's
width instead. Its notes then no longer sit under the columns above.
Display only; a row that already fits is unchanged.
"flush": true aligns a row left, leaving the opening bar's column clear; display only.
Example
{ "svara": ["madhyam", "m"], "lyric": "অ" } // pitch + lyric
{ "svara": ["madhyam", "p"], "komal": true } // flat
{ "sustain": true, "lyric": "৹" } // আ-কার hold
{ "bar": "single" } // দাঁড়ি
Worked examples:
akarone,
octaves,
microtones,
matra,
brackets. Full schema:
src/spec/schema.ts.