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
| Field | Type | Description |
|---|---|---|
| lines | (string | LineInput)[] required | Text 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][] optional | Word-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. |
| settings | SettingsInput optional | Global visual overrides: palette, line style, thickness, opacity, background. See Visual settings. |
| pairs | PairInput[] optional | Per-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:
| Field | Type | Default | Description |
|---|---|---|---|
| text | string required | — | Line text. |
| font | string | Inter | Google Fonts family name, e.g. "Noto Serif", "Noto Sans Arabic", "Noto Sans Hebrew". |
| sizePx | integer 12–64 | 36 | Text size in px. |
| gapPx | integer 0–56 | 14 | Horizontal gap between word tokens in px. |
| rtl | boolean | false | Right-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.
| Field | Values | Default | Description |
|---|---|---|---|
| palette | pastel vivid academic | pastel | Color palette for connection lines and token tints. |
| lineStyle | curved straight | curved | Shape of connection lines. |
| lineThickness | number 1–8 | 3 | Stroke width of connection lines. |
| lineOpacity | number 0.2–1 | 1 | Opacity of connection lines. |
| background | light dark | light | Preview background color. |
| theme | light dark | light | UI theme (affects token chip color). |
| showNumbers | boolean | false | Show line numbers next to each line. |
| colorTokensByLink | boolean | true | Tint word tokens in the color of their connection. |
| tokenSplitChars | string | .-| | 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). |
| tokenMergeChar | string (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).
| Field | Type | Default | Description |
|---|---|---|---|
| upper | integer required | — | 0-based index of the upper line. |
| lower | integer required | — | 0-based index of the lower line (must equal upper + 1). |
| gapPx | integer 12–156 | 120 | Vertical gap between the two lines in px. |
| showConnectors | boolean | true | When 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":
| Index | Word |
|---|---|
| 0 | Bonjour |
| 1 | le |
| 2 | monde |
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: *).