swaralipi-js

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:

FieldMeaning
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)

FlagMeaning
komal / tivraflat / sharp
microtone: "alpakomal" | "atikomal"below komal
durationMatra: 0.5fractional 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?: booleanfalse hides that note’s া; visual only, with duration and playback unchanged. Rests always show their sign.
lyric: "অ"per-syllable lyric (column-aligned)
kan / reshgrace 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 / belowrecursive 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.