Skip to content

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

KeyTypeWhat 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).

KeyTypeWhat 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 a line/range target and line counts lyric lines only (0-based) — the app resolves it to the row on load.
  • GapgapAfter: n is the boundary after stanza n; -1 is 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

KeyTypeDefaultWhat 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

TargetIndicesAnchors 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

kindWhat it drawsTargetsIts 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

KeyTypeDefaultWhat 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 like gapAfter (-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.

KeyValuesDefaultWhat 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

KeyValuesDefaultWhat 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.

KeyValuesDefaultWhat 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).

KeyValuesDefaultWhat 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.

KeyValuesDefaultWhat 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

KeyValuesDefaultWhat 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.

KeyValuesDefaultWhat 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

KeyValuesDefaultWhat 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.

KeyValuesDefaultWhat 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

KeyValuesDefaultWhat 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 properties and body right, 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 color and 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.