API

One endpoint: send text lines and optional alignment pairs, get back a shareable Word Aligner URL. No API key, no sign-up.

OpenAPI schema: /api/align/openapi.json

POST /api/align

The main endpoint. Returns a URL to Word Aligner with the given lines and alignment links pre-filled.

Request body

Content-Type: application/json

FieldTypeDescription
lines(string | LineInput)[] requiredText lines, top to bottom. 1–8 entries. Each may be a plain string or a LineInput object with per-line visual options.
alignments[int,int,int,int][] optionalWord-alignment pairs as [lineA, wordA, lineB, wordB]. Lines A and B must be adjacent (|A−B| = 1). Indices are 0-based. Multiple pairs can share the same word (many-to-one) — they receive the same color automatically.
settingsSettingsInput optionalGlobal visual overrides: palette, line style, thickness, opacity, background. See Visual settings.
pairsPairInput[] optionalPer-pair controls: vertical gap between adjacent lines or hiding connectors for a specific pair. See Pair controls.

Response

{ "url": "https://aligner.tinygods.dev/?data=..." }

Example — simple alignment

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Hello world", "Bonjour le monde"],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1],
      [0, 1, 1, 2]
    ]
  }'

Links "Hello" → "Bonjour" (word 0 → word 0). "world" → "le" + "monde" (words 1 and 2) — the French article is part of the noun phrase, so both words share the same color.

Example — many-to-one (one word maps to several)

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Я ходил", "I have been going"],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1],
      [0, 1, 1, 2],
      [0, 1, 1, 3]
    ]
  }'

"ходил" (word 1) maps to three English words. All three connections share the same color automatically.

GET /api/align

Simple variant: pass lines as repeated query parameters. No alignments. Useful for opening the editor pre-filled via a plain link.

GET /api/align?lines=Hello+world&lines=Bonjour+le+monde

Parameter: lines — repeat for each line (1–8).

Per-line options (LineInput)

Instead of a plain string, each entry in lines can be an object:

FieldTypeDefaultDescription
textstring requiredLine text.
fontstringInterGoogle Fonts family name, e.g. "Noto Serif", "Noto Sans Arabic", "Noto Sans Hebrew".
sizePxinteger 12–6436Text size in px.
gapPxinteger 0–5614Horizontal gap between word tokens in px.
rtlbooleanfalseRight-to-left layout. Use for Hebrew, Arabic, Farsi, Urdu, etc. Word indices remain 0-based left-to-right in logical order.

Example — Hebrew with RTL and custom font

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "text": "שלום עולם", "rtl": true, "sizePx": 48, "font": "Noto Sans Hebrew" },
      { "text": "Hello world", "sizePx": 40 }
    ],
    "alignments": [
      [0, 0, 1, 0],
      [0, 1, 1, 1]
    ]
  }'

Visual settings (SettingsInput)

The optional settings object overrides global visual parameters. Unset fields inherit defaults.

FieldValuesDefaultDescription
palettepastel vivid academicpastelColor palette for connection lines and token tints.
lineStylecurved straightcurvedShape of connection lines.
lineThicknessnumber 1–83Stroke width of connection lines.
lineOpacitynumber 0.2–11Opacity of connection lines.
backgroundlight darklightPreview background color.
themelight darklightUI theme (affects token chip color).
showNumbersbooleanfalseShow line numbers next to each line.
colorTokensByLinkbooleantrueTint word tokens in the color of their connection.
tokenSplitCharsstring.-|Characters (besides whitespace) that split text into tokens. The split character is not rendered. Set to -| to keep periods inside Leipzig gloss morphemes (e.g. go.PST.IPFV stays one token).
tokenMergeCharstring (1 char)+Joins parts into one token while rendering as a space, e.g. is+playing → "is playing" (one word).

Example — vivid palette, straight lines, dark background

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": ["Hello world", "Bonjour le monde"],
    "alignments": [[0, 0, 1, 0], [0, 1, 1, 2]],
    "settings": {
      "palette": "vivid",
      "lineStyle": "straight",
      "background": "dark",
      "theme": "dark",
      "lineThickness": 2
    }
  }'

Pair controls (PairInput)

The optional pairs array lets you adjust the vertical gap between specific adjacent line pairs, or hide connectors entirely for a pair (useful when some lines are glosses that annotate but do not align to the line above).

FieldTypeDefaultDescription
upperinteger required0-based index of the upper line.
lowerinteger required0-based index of the lower line (must equal upper + 1).
gapPxinteger 12–156120Vertical gap between the two lines in px.
showConnectorsbooleantrueWhen false, connection lines are not drawn between this pair. Alignment data is still encoded.

Example — interlinear (Leipzig) gloss

curl -X POST https://aligner.tinygods.dev/api/align \
  -H "Content-Type: application/json" \
  -d '{
    "lines": [
      { "text": "1SG.NOM go.PST.IPFV", "sizePx": 22 },
      { "text": "Я ходил", "sizePx": 40 },
      { "text": "I have been going", "sizePx": 36 }
    ],
    "alignments": [
      [0, 0, 1, 0], [0, 1, 1, 1],
      [1, 0, 2, 0],
      [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3]
    ],
    "settings": { "tokenSplitChars": "-|" },
    "pairs": [
      { "upper": 0, "lower": 1, "gapPx": 12, "showConnectors": false }
    ]
  }'

The gloss tier sits directly above the source (lines 0–1) with a tight 12 px gap and hidden arcs — its tokens are color-matched to the source words but no lines are drawn. The source→translation pair keeps its arcs. Note "tokenSplitChars": "-|": this drops the period from the split set so a Leipzig morpheme like go.PST.IPFV stays a single token instead of rendering as goPSTIPFV.

Word indices and tokenization

Words are counted from 0, left to right as written (logical order — even for RTL lines). The default split rules: whitespace always splits, and the characters . - | also create word boundaries. Punctuation is not split into separate tokens by default.

The split character itself is not rendered — it is consumed as a boundary. So go.PST.IPFV with the default split set displays as three tokens go PST IPFV with the dots removed. If you need the period to stay (common in Leipzig glosses), override settings.tokenSplitChars to "-|".

Example — "Bonjour le monde":

IndexWord
0Bonjour
1le
2monde

If you are unsure about word counts, use the GET endpoint without alignments first — open the returned URL and count the word boxes in the editor.

Errors

Errors return HTTP 400 with a JSON body:

{ "error": "alignments[0]: lines 0 and 2 are not adjacent" }

All endpoints support CORS (Access-Control-Allow-Origin: *).