Format reference
The .gigchart file, in full
Everything a chart carries, key by key — so an assistant (or a script, or you in a text editor) can write a finished, fully annotated song, not just its words and chords.
A .gigchart is one JSON file. It holds the song, its annotations and the exact way
it should be typeset, and it is the whole asset: hand it to another machine and the chart comes
out identical. This page documents every part of it.
If you only want to get a song in, Author with AI is the shorter read — ChordPro text covers words, chords, sections and cues. Come here when you want the rest: exact placement, colors, voices, road maps, page geometry.
The envelope
{
"format": "gigchart", // always this literal
"version": 6, // current file version
"savedAt": 1753228800000, // optional, ms epoch
"song": { … } // everything below
} The smallest file that opens and renders:
{
"format": "gigchart",
"version": 6,
"song": {
"properties": { "title": "House of the Rising Sun", "artist": "Traditional" },
"body": [
"Am C D F",
"There is a house in New Orleans",
"",
"Am C D F",
"My mother was a tailor"
]
}
} version is the file format's version, not the song's. Write 5.
Older files still load — the app migrates them — but new producers should emit the current
shape. Unknown keys are ignored rather than rejected, so a file written against a later
version still opens.
What song contains
| Key | Type | What it is |
|---|---|---|
properties | object | Song identity — title, artist, key, tempo… Everything here is optional. |
body | string[] | The chart itself, one array entry per line. "" separates stanzas. |
settings | object | Presentation, grouped by function. Omit it entirely and the app uses its defaults. |
annotations | object[] | Cues, sections, repeats, road maps, voices, capos, page breaks. |
spacers | object[] | Explicit blocks of vertical space. Omit it: the app builds them from your blank lines and gap annotations. |
voices | object | { roster: [{ id, name, color }], defaultVoiceId } — the people whose lines get colored. |
textStyles | object | Inline italic / underline / voice-colour runs over the body, in flat character offsets: { italic, underline, voice }. (Files before v6 used the short keys i / u / v; still read.) |
palette | object | A custom color theme traveling with the song. |
properties — the song's identity
Free-text fields, all optional, all strings unless noted. These are the same values the
ChordPro directives on the import page fill in — only the key spelling
differs ({key: Am} here is originalKey).
| Key | Type | What it does |
|---|---|---|
title | string | Song title. |
artist | string | Performing artist. |
subtitle | string | Free subtitle text — its own field, not the artist. |
music | string | Composer credit. |
lyrics | string | Lyricist credit. |
arranger | string | Arranger credit. |
tempo | string | number | Tempo in BPM. Stored internally as bpm; write tempo. |
timeSig | string | Time signature, e.g. 6/8. |
style | string | Beat / style, e.g. Cha cha. Shown as “beat” in the info box. |
genre | string | Genre. |
originalKey | string | The key the chart is written in. |
keyActual | string | The sounding key, when a capo makes it differ. |
capo | string | Capo fret as text ("2"). A non-numeric value means no concert transpose. |
capoMode | '' | 'capo' | 'concert' | Whether the chart is written capo-relative or at concert pitch. |
targetInstrument | string | Instrument the chart is for. |
revision | string | The chart's own revision, printed in the bottom box. (Files before v6 spelled this version, which collided with the envelope's format version; that spelling is still read.) |
video | string (URL) | Video link — printed as a link or a QR code (see settings.infoBox1.videoMode). |
album | string | Album name. |
track | string | Track number. |
year | string | Year. |
copyright | string | Copyright line. |
duration | string | Duration, e.g. 3:45. |
Nothing here is printed just because it is set: the header prints what its
title and subtitle templates ask for, and the info box prints the
fields it is told to show. Setting album or copyright stores them
for later without changing the page.
body — the chart itself
An array of lines: ["chord line", "lyric line", "", …]. One entry per line, no
trailing newline, and an empty string separates stanzas. Two chord notations work, and they
can be mixed:
- Chords above lyrics — a chord-only line whose columns sit over the syllable they change on, then its lyric line. Columns are what align, so this only works in a monospace font — which is exactly what GigCharts renders in.
- Inline —
[Am]There is a [C]house. The renderer splits each such line into a chord row and a lyric row.
Stanzas, rows and gaps
Annotations don't point at characters; they point at stanzas, rows inside a stanza, and the gaps between stanzas. All three are 0-based and all three are counted the same way everywhere in the file:
stanza 0 ┌ row 0 Am C D F ← chord row
│ row 1 There is a house in New Orleans ← lyric row
│ row 2 Am C E
└ row 3 They call the Rising Sun
← gapAfter 0
stanza 1 ┌ row 0 Am C D F
└ row 1 My mother was a tailor - Stanza — a run of non-blank lines. Blank lines separate them; they aren't stanzas themselves.
- Row — a rendered row inside the stanza. A chord row and a lyric row are two
rows, so in the usual chord-over-lyric chart the lyric rows are the odd
indices (1, 3, 5…). A wrapped line adds rows. To skip the counting, add
lineBasis: 'lyric'to aline/rangetarget andlinecounts lyric lines only (0-based) — the app resolves it to the row on load. - Gap —
gapAfter: nis the boundary after stanzan;-1is the space above the first stanza.
annotations — everything drawn over the music
A flat array. Each entry is one widget: what it is (kind), where it goes
(target), and its own fields.
{
"id": "a-cue-1",
"kind": "cue",
"text": "slower",
"target": { "kind": "line", "stanza": 0, "line": 1 },
"placement": "inline",
"color": "#e23b3b"
} Fields every annotation shares
| Key | Type | Default | What it does |
|---|---|---|---|
id | string | — | Unique within the song. Any stable string works. |
kind | AnnKind | — | One of the nine kinds below. |
target | object | — | What it is anchored to — see the target table. |
hidden | boolean | false | Kept in the file and the Layers panel, but not drawn and not exported. |
color | hex string | the theme's color for that kind | Base color (a hex string). Files from older app versions still load unchanged. |
offset | { dx, dy, atFont? } | none | Free-drag nudge in px off the computed spot. atFont is the body pt size it was placed at, so the nudge scales with reflow. |
anchor | 'row' | 'page' | 'row' | 'row' follows the music through a reflow; 'page' pins the widget to the page. |
pagePos | { x, y } | none | Frozen page-box position, used only when anchor is 'page'. |
Colors serialize as color — on annotations, on bar items and on
voices. Leave a color out and the element follows the chart's theme, which is usually what you
want.
Targets
| Target | Indices | Anchors to |
|---|---|---|
{ kind: 'stanza', stanza } | 0-based stanza index | The whole stanza. |
{ kind: 'span', from, to } | inclusive stanza range | A run of stanzas — one spine or bar covering several. |
{ kind: 'line', stanza, line } | row index inside the stanza | One rendered row. Rows alternate chord, lyric, chord, lyric… so lyric rows are the odd indices. Add lineBasis: 'lyric' to count lyric lines only (0-based) and let the app resolve the row for you. |
{ kind: 'range', stanza, line, from, to } | row + column range | A word range on one lyric row; from/to are character columns in that row. Also accepts lineBasis: 'lyric'. |
{ kind: 'gap', gapAfter } | boundary index | The gap after stanza gapAfter (-1 = before the first stanza). Use this — the app converts it to a spacer on load. |
{ kind: 'spacer', spacerId } | spacer id | A gap annotation already hosted by an explicit spacer. What the app writes when it saves. |
Gap-scoped kinds — cue, road map, repeat-section mark, page break — are stored against a
spacer once the app has loaded them. You don't have to build those:
write { "kind": "gap", "gapAfter": n } and the app mints the spacer,
gives it a sensible height, and rewrites the target on load.
The nine kinds
| kind | What it draws | Targets | Its own fields |
|---|---|---|---|
section | A labeled spine beside a stanza or a run of stanzas (Verse, Chorus, Bridge…). | stanza, span | text (the label), placement 'left' | 'inline' | 'right', textDir 'left' | 'right' | 'stack', orient 'auto' | 'horizontal' | 'vertical' |
cue | A small labeled chip — a performance note. The most-used annotation. | line, stanza, gap / spacer | text (\n makes a multi-line chip), placement — on a stanza 'above' | 'inline' | 'beside' | 'right', on a line 'inline' | 'right', in a gap 'left' | 'inline' (center) | 'right' |
repeat | A repeat bar with a ×N count beside a stanza. | stanza, span | text = the count ("2" renders 2×), placement 'left' | 'inline' | 'right' |
markers | A group of narrow vertical bars in a gutter — who plays what, side by side. | stanza, span | items[] (the bars), textDir (adds 'normal' = a horizontal chip), spacing 0 | 1 | 2, placement |
roadmap | An arrangement list: stacked bars in a gap (Intro · Verse ×4 · Outro). | gap / spacer | items[], align 'left' | 'center' | 'right' (text inside the bars), roadmapWidth 'full' | 'longest', placement (only when 'longest') |
recall | A “Repeat: …” recall mark in a gap, pointing at an earlier stanza — or that stanza's full text inlined. | gap / spacer | refStanza (which stanza to recall), text (overrides the “Repeat:” prefix), showRecallText, hideChip, reserveLines 1–3, placement |
capo | A per-stanza capo pill — retunes the concert chords from that stanza on. | stanza | text = the fret ("5"), placement 'above' | 'inline'. 'above' reserves a body row; 'inline' costs no page room. |
voice | Colors lyrics in a voice's color — a whole stanza, one line, or a word range. | stanza, line, range | voiceId — an id from song.voices.roster. No text, no color (the voice supplies it). |
pagebreak | Forces a page break at that gap. | gap / spacer | none — the target is the whole annotation. |
At most one section, repeat and capo per
stanza — extras are dropped on load. Cues, marker groups and voices may stack freely.
The bar items of a road map or marker group
| Key | Type | Default | What it does |
|---|---|---|---|
id | string | — | Unique within the annotation. |
label | string | — | The text in the bar. |
repeat | number | none | Renders a ×N after the label. |
color | hex string | the annotation's color | Overrides the base color for this bar alone. |
hidden | boolean | false | Keeps the bar in the file without drawing it. |
One of each
// A section spine over stanzas 1–2, in the left gutter
{ "id": "s1", "kind": "section", "text": "Verses 2–3",
"target": { "kind": "span", "from": 1, "to": 2 },
"placement": "left", "textDir": "left" }
// A two-line cue chip centered in the gap after stanza 1
{ "id": "c1", "kind": "cue", "text": "instrumental\nverse",
"target": { "kind": "gap", "gapAfter": 1 },
"placement": "inline", "color": "#1aa64b" }
// Play stanza 3 twice
{ "id": "r1", "kind": "repeat", "text": "2",
"target": { "kind": "stanza", "stanza": 3 }, "placement": "inline" }
// Capo 5 from stanza 2 on
{ "id": "k1", "kind": "capo", "text": "5",
"target": { "kind": "stanza", "stanza": 2 }, "placement": "above" }
// Two marker bars in the right gutter
{ "id": "m1", "kind": "markers",
"target": { "kind": "stanza", "stanza": 1 },
"placement": "right", "textDir": "left", "spacing": 1, "color": "#ef8a2b",
"items": [ { "id": "b1", "label": "guitar", "repeat": 2 },
{ "id": "b2", "label": "shaker", "color": "#20b7d4" } ] }
// The arrangement, as a full-width block after the last stanza
{ "id": "rm1", "kind": "roadmap",
"target": { "kind": "gap", "gapAfter": 3 },
"align": "center", "roadmapWidth": "full", "color": "#7ac043",
"items": [ { "id": "i1", "label": "Intro" },
{ "id": "i2", "label": "Verse", "repeat": 4 },
{ "id": "i3", "label": "Outro", "color": "#6c4bd1" } ] }
// "Repeat: Verse 1" in the gap after stanza 2, two rows tall
{ "id": "ch1", "kind": "recall",
"target": { "kind": "gap", "gapAfter": 2 },
"refStanza": 0, "placement": "inline", "reserveLines": 2 }
// Force a new page after stanza 1
{ "id": "pb1", "kind": "pagebreak", "target": { "kind": "gap", "gapAfter": 1 } } spacers — vertical space as an object
A spacer is a block of empty rows at one boundary. It owns its height and hosts at most one gap-scoped annotation; several can stack at the same boundary. That's how a chart can have a three-row gap before the bridge, or a cue chip with room around it.
"spacers": [
{ "id": "sp-1", "after": -1, "reserveLines": 1 }, // above the first stanza
{ "id": "sp-2", "after": 0, "reserveLines": 2 }, // a double gap after stanza 0
{ "id": "sp-3", "after": 1, "reserveLines": 3 } // hosts a 2-line cue chip
],
"annotations": [
{ "id": "c1", "kind": "cue", "text": "instrumental\nverse",
"target": { "kind": "spacer", "spacerId": "sp-3" } }
] after— the boundary, numbered likegapAfter(-1= above the first stanza).reserveLines— the block's height in body rows.hidden— kept in the file, reserving nothing.
voices — who sings what
A song owns its voices: name and color travel in the file, so the coloring survives on any
machine. roster entries are { id, name, color }; annotations
refer to them by voiceId. Use ids of the form v-001,
v-002… (all is reserved for the built-in “All” voice).
"annotations": [
// the whole second row of stanza 1 in Bea's color
{ "id": "v1", "kind": "voice", "voiceId": "v-001",
"target": { "kind": "line", "stanza": 1, "line": 1 } },
// just the first nine characters of row 3
{ "id": "v2", "kind": "voice", "voiceId": "v-002",
"target": { "kind": "range", "stanza": 1, "line": 3, "from": 0, "to": 9 } }
],
"voices": {
"roster": [
{ "id": "v-001", "name": "Bea", "color": "#c45912" },
{ "id": "v-002", "name": "Atti", "color": "#2c5fd6" }
],
"defaultVoiceId": null
} defaultVoiceId is the song's default voice, or null. A
range target's from/to are character columns in that
row's text — count them in the line as you wrote it in body.
textStyles — italic and underline
Inline styling can't live in body — markup there would shift the monospace
columns and pull the chords off their syllables. So it rides alongside, as character ranges
over the body treated as one flat string (lines joined with \n, exactly as a text
editor would count).
"textStyles": {
"italic": [[28, 59]], // "There is a house in New Orleans"
"underline": [[48, 59]], // "New Orleans"
"voice": [[60, 84, "#c45912"]] // a raw color run (voices are better done as annotations)
} i = italic, u = underline, v = colored runs. Ranges are
half-open [start, end). They may cover chord rows too — usually you don't want
that, so count carefully, or set styling in the app and save from there.
settings — how it is typeset
Grouped by function, and every group (and every key inside it) is optional. Omit a group and nothing about it changes.
"settings": {
"typography": { "fontKey": "dejavu", "fontSizePt": 12, "layout": "vertical" },
"header": { "titleColor": "#2c5fd6" },
"concert": { "showConcert": true, "concertBoxMode": "page" }
} Values are validated on load: an out-of-range enum falls back to a safe value instead of breaking the chart, and numeric ranges are clamped. So a wrong value doesn't destroy the file — it just quietly isn't what you asked for.
settings.page
Page geometry and the page-turn preview band. A chart carries its own page, so it opens the same size on any machine.
| Key | Values | Default | What it does |
|---|---|---|---|
presetId | string | 'ipadpro13' | Factory page: letter, halfletter, a4, a5, macbook15, macbook13, ipadpro13, ipadpro11, hd, 4k, 5k2k, iphone17, iphone17max, pixel10, pixel10xl. |
pagePreset | object | — | The resolved page, embedded so it travels: { id, name, unit: 'in'|'mm'|'px', width, height, ppi?, marginMode: 'unit'|'percent', margins: { top, right, bottom, left }, marginsLinked, orientation }. Wins over presetId; drop it and the id is used. |
orientation | 'portrait' | 'landscape' | 'portrait' | Overrides the preset's own orientation. |
pageTurnPreview | number | 0 | Rows of the next page repeated at the bottom of the previous one. 0 = off. |
pageTurnPos | 'bottom' | 'flow' | 'bottom' | Band pinned to the page bottom, or placed in the flow. |
pageTurnBorder / pageTurnFill | hex | '' | '#7c3aed' / '' | Border and fill of the repeated band. '' = none. |
pageTurnNextBorder / pageTurnNextFill | hex | '' | mirrors the band | The frame drawn around the original lines on the next page. |
pageTurnDy | number | 0 | Whole-row vertical nudge of the band. |
pageTurnFrameTop | boolean | false | Paint the next-page frame over the other elements instead of behind them. |
settings.typography
| Key | Values | Default | What it does |
|---|---|---|---|
fontKey | string | 'dejavu' | dejavu, jetbrains, sourcecodepro, and the Pro faces courierprime, intelone, recmono, ubuntumono. Only DejaVu and JetBrains carry ♯/♭ glyphs. |
fontSizePt | number 5–30 | 11 | Body size. The single biggest lever on page count. |
lineSpacing | number 1.1–1.6 | 1.32 | Line height as a multiple of the em. Clamped on load. |
chordWeight / lyricWeight | 400 | 700 | 700 / 700 | Per-role body weight. |
layout | 'vertical' | 'horizontal' | 'booklet' | 'horizontal' | How multiple pages are laid out on the stage. |
overflow | 'break' | 'fit' | 'kern' | 'hide' | 'break' | What a too-wide line does: wrap, shrink to fit, tighten letter spacing, or clip. |
letStanzasBreak | boolean | false | Allow a stanza to split across a page break. |
settings.labels
The printed wording of every prefixed field. House style — but it prints, so it travels with the song. Set any of them to '' to print the bare value.
| Key | Values | Default | What it does |
|---|---|---|---|
tempoPrefix | string | 'Tempo:' | Before the BPM in the info box. |
timeSigPrefix | string | 'Time:' | Before the time signature. |
beatPrefix | string | '' | Before the beat / style. |
originalKeyPrefix | string | 'Original key' | Before the original key. |
genrePrefix | string | '' | Before the genre. |
instrumentPrefix | string | 'Instr:' | Before the target instrument. |
versionPrefix | string | 'v.' | Before the version in the bottom box. |
pageNumberPrefix | string | 'p ' | Before the page number. |
capoPrefix | string | 'Capo' | On the capo pill. |
videoPrefix | string | '▶' | Before the video link. |
settings.header
Title and subtitle are template-driven: each is an ordered list of { field } and { sep } tokens. An empty array means the default composition (title = artist – title).
| Key | Values | Default | What it does |
|---|---|---|---|
showTitle / showSubtitle | boolean | true | Print the line at all. |
titleTemplate / subtitleTemplate | TplToken[] | [] | e.g. [{"field":"title"},{"sep":" — "},{"field":"artist"}]. Fields: title, artist, subtitle, music, lyrics, arranger, album, track, year, genre, style, bpm, timeSig, originalKey, keyActual, capo, version, copyright, duration, targetInstrument, video. An empty field drops its separator with it. |
titleSizeMode / subtitleSizeMode | 'body' | 'offset' | 'percent' | 'fixed' | 'body' / 'percent' | How the line is sized relative to the body text. |
titleSizeOffset / subtitleSizeOffset | number −10…+10 | 2 / 0 | Points added to the body size in 'offset' mode. |
titleSizePct / subtitleSizePct | number 30–300 | 100 / 75 | Percentage of the body size in 'percent' mode. |
titleSizePt / subtitleSizePt | number 5–72 | 13 / 10 | Absolute size in 'fixed' mode. |
titleColor / subtitleColor | hex | '' | '' | '' inherits the body color. |
titleAlign / subtitleAlign | 'left' | 'center' | 'right' | 'left' | Horizontal alignment on the page. |
titleOffset / subtitleOffset | { dx, dy } | { dx: 0, dy: 0 } | Drag nudge off the anchor. |
titlePagePos / subtitlePagePos | { x, y } | null | null | Pin to an absolute page position. |
metaPos | 'left' | 'right' | 'right' | Which top corner the info box sits in. |
metaOffset / metaPagePos | { dx, dy } / { x, y } | null | { 0, 0 } / null | Info-box nudge and page pin. |
settings.infoBox1
The meta box in the top corner. Each field shows only when the matching property has a value.
| Key | Values | Default | What it does |
|---|---|---|---|
showInfoBox | boolean | true | The box as a whole. |
infoBoxOrder | string[] | ['tempo','timeSig','beat','originalKey','genre','instrument'] | Display order. Unknown keys are dropped and missing ones appended. |
showBpm / showTimeSig / showBeat / showOriginalKey / showGenre / showInstrument | boolean | true | Per-field visibility. |
showVersion | boolean | true | The version in the bottom box (it lives in this group for historical reasons). |
videoMode | 'off' | 'link' | 'qr' | 'off' | Print the video property as a link or a QR code. |
videoPos | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'top-right' | 'bottom-left' | Where the link / QR sits. |
videoScale | number | 1 | QR size multiplier. |
videoOffset / videoPagePos | { dx, dy } / { x, y } | null | { 0, 0 } / null | Nudge and page pin. |
settings.bottomBox
| Key | Values | Default | What it does |
|---|---|---|---|
showBottomBox | boolean | true | The footer badge (version + page number). |
showPageNumber | boolean | true | The page number inside it. |
footerPos | 'left' | 'center' | 'right' | 'right' | Which bottom corner. |
footerOffset / footerPagePos | { dx, dy } / { x, y } | null | { 0, 0 } / null | Nudge and page pin. |
settings.capo
The song-wide capo pill (the capo property is its value). Per-stanza capos are annotations instead.
| Key | Values | Default | What it does |
|---|---|---|---|
showCapo | boolean | true | Print the pill. |
capoPos | 'left' | 'inline' | 'right' | 'left' | Alignment in its band — 'inline' reads as center. |
capoRows | number | 1 | Extra blank rows under the pill; the pill always owns one row itself. |
capoStack | number | 0 | Its index in the stack of things above the first stanza. |
capoColor | hex | '' | '' | '' inherits the theme's capo swatch. |
capoOffset / capoPagePos | { dx, dy } / { x, y } | null | { 0, 0 } / null | Nudge and page pin. |
settings.notation
| Key | Values | Default | What it does |
|---|---|---|---|
srcNoteSel | 'english' | 'german' | null | null | How the chords in body are written. null = auto-detect. German reads H as B natural. |
srcMinorSel | 'english' | 'hungarian' | null | null | Am vs the folk lowercase a in the source. |
outNoteSel / outMinorSel | same values | null | How chords are printed. null = same as the source. |
prettyAcc | boolean | false | Print ♯/♭ instead of #/b. Only DejaVu and JetBrains have the glyphs. |
transpose | number | 0 | Semitones, ±7 in the UI. |
settings.concert
The side column of concert-pitch chords, for a capo'd chart played with someone who isn't capo'd.
| Key | Values | Default | What it does |
|---|---|---|---|
showConcert | boolean | false | Show the concert column. |
concertBoxMode | 'page' | 'stanza' | 'page' | One gray column down the page, or a tinted box per stanza. |
concertGap | number | 2 | Gap in characters between the chart and the column. |
concertAlign | 'beat' | 'pack' | 'beat' | Line the concert chords up with the beat, or pack them tight. |
concertTitleShow / concertTitle | boolean / string | true / 'CONCERT' | The column's heading. |
settings.annotations
| Key | Values | Default | What it does |
|---|---|---|---|
annotFontSep | boolean | false | Size annotation text separately from the body. |
annotFontSizePt | number | 9 | That separate size. |
snapGrid | 'off' | 'whole' | 'half' | 'whole' | Row grid that dragged annotations snap to. |
palette — a color theme in the file
Optional, and only present when the song uses a custom theme (the four factory themes — Rainbow, Pastel, Muted, Grayscale — are not written into files). A palette is twelve editable swatches plus an assignment of each chart element to a swatch index.
"palette": {
"id": "my-theme",
"name": "My theme",
"swatches": [ { "bg": "#e23b3b" }, { "bg": "#ef8a2b" }, … 12 in total … ],
"assign": { "cue": 0, "section": 4, "roadmap": 3, "markers": 1 }
}
Assignable elements: title, subtitle, body,
chord, capo, repeat, section,
cue, markers, roadmap, pagebreak,
chorus, concertbox, concertboxtitle,
concertchords, infobox, bottombox,
videolink, qr, pageturnborder, pageturnbg,
pageturnnextborder, pageturnnextbg. Index 12 is the
theme's rainbow swatch, valid only for elements with a solid fill.
Per-element colors on the chart are simpler to set directly (color on the
annotation, titleColor in the header group) — reach for a palette only when you
want a whole coordinated theme.
A complete song
Download the showcase file — one song using every element on this page: section spines, cues on a line and in a gap, a repeat bar, a per-stanza capo, a marker group, a road map, a chorus recall, line and word-range voices, inline styling, and every settings group. Open it in the app with Open file, take it apart, and copy what you need.
It's also the file this page was checked against: it was loaded into GigCharts and every element confirmed on the rendered page before any of this was written.
Rules of thumb
- Omit before guessing. An absent key means “use the default”. A wrong one means a chart that isn't what you meant.
- Start from the words. Get
propertiesandbodyright, then add annotations. They anchor to stanzas and rows, so the body has to be settled first. - Count stanzas from 0, and remember lyric rows are the odd row indices in a chords-above chart.
- Use
gapAfter, not spacers, unless you specifically want an unusual gap height. - Colors are
colorand hex. Leave them out to follow the theme. - Page count is an outcome, not a setting. Font size, line spacing and page preset drive it; the final break depends on the render, so the author checks it in the app.
- Round-trip to be sure. Open the file in the app, adjust, then Save → .gigchart — what comes out is the canonical form of what you wrote.
Ready to hand one over? Build a one-click import link and the song opens straight in the studio.