# JSON Format Reference

This is the living reference for the app's board and tile import/export data.

The examples in this document use JSONC-style comments so the structure is easy to read. The app import field expects valid JSON, so remove comments before importing.

## Import Behavior

Imports are forgiving:

- Missing attributes do not block the import.
- Empty attributes do not block the import.
- Unknown attributes are ignored.
- Invalid or unsupported values fall back to defaults.
- Invalid JSON text cannot be imported.
- A board import replaces the current board after confirmation and clears runtime selection state.
- A tile import appends tile records to the current board.
- If a tile is imported with no attributes, an empty/default tile is created.
- Board and tile imports can optionally include assets used by `:token:` text and Image tiles.
- Asset pack imports add assets without changing the board.
- Backup imports replace the local session and the local asset library after confirmation.
- Selective Import can import only chosen sections from supported JSON, such as tiles, assets, settings groups, drawer tiles, hotkeys, pinned controls, UI layout, or notepad items.
- If an imported asset token conflicts with a different local asset, the imported token is renamed and imported board/tile text is updated to match.
- Image assets must be self-contained base64 `data:` URLs. Remote URLs are ignored.
- Supported image asset MIME types are `image/png`, `image/jpeg`, `image/webp`, and `image/gif`.
- Image assets larger than 8 MB, or asset batches larger than 40 MB, trigger a confirmation warning before import.

## Sharing Choices

- Use **Board Export** for a board plus board settings. Turn **Include Used Assets** on when the board should render image tokens and Image tiles on another device without a separate asset pack.
- Use **Export Tile** for the selected tile. Turn **Include Used Assets** on when that tile uses image tokens or an Image tile asset.
- Use **Asset Pack** from the Assets panel to share assets without changing anyone's board.
- Use **Export Backup** when you want to move or archive the whole local RNGED session, including board, drawer, notes, pinned controls, hotkeys, settings, and all assets.

## Supported Top-Level Shapes

### Board Export Shape

```jsonc
{
  "board": {
    "version": 2,
    "settings": {
      "layout": "auto-grid",
      "mode": "flyover",
      "grid": {
        "columns": 4,
        "rows": 4
      },
      "visibleTiles": 7,
      "showStagger": true,
      "showTurnButton": true,
      "tileDisplay": {
        "front": {
          "name": true,
          "preTitle": false,
          "subtitle": true,
          "extra": false
        },
        "back": {
          "name": true,
          "preTitle": true,
          "subtitle": true,
          "extra": true
        }
      },
      "syncWinnerFlipToBoard": false
    },
    // Optional. Present when "Include Used Assets" is enabled.
    "assets": [
      {
        "id": "crown",
        "name": "Crown",
        "token": "crown",
        "kind": "image",
        "mime": "image/png",
        "data": "data:image/png;base64,..."
      }
    ],
    "tiles": [
      {
        "type": "image",
        "imageToken": "crown",
        "hideTextOnBoard": false,
        "hideTextOnWinner": false,
        "title": "Rainbow Road",
        "subtitle": "Final cup"
      }
    ]
  }
}
```

### Shortcut Board Shape

```jsonc
{
  "tiles": [
    { "title": "Track 1" },
    { "title": "Track 2" }
  ]
}
```

### Single Tile Wrapper

```jsonc
{
  "tile": {
    "title": "Single imported tile :crown:"
  },
  "assets": [
    {
      "name": "Crown",
      "token": "crown",
      "kind": "image",
      "mime": "image/png",
      "data": "data:image/png;base64,..."
    }
  ]
}
```

### Tile Array

```jsonc
[
  { "title": "Tile A" },
  { "title": "Tile B" }
]
```

### Single Tile Object

```jsonc
{
  "title": "Title-only tile"
}
```

### Empty Tile

```jsonc
{
  "tile": {}
}
```

### Asset Pack

```jsonc
{
  "rngedAssetPack": {
    "version": 1,
    "name": "Example Asset Pack",
    "assets": [
      {
        "id": "crown",
        "name": "Crown",
        "token": "crown",
        "kind": "image",
        "mime": "image/png",
        "data": "data:image/png;base64,..."
      }
    ]
  }
}
```

### Full Backup

```jsonc
{
  "rngedBackup": {
    "version": 1,
    "exportedAt": "2026-06-20T12:00:00.000Z",

    // Full local session snapshot: board, drawer, board settings, UI state,
    // notepad items, pinned controls, hotkeys, last winners, and related options.
    "session": {
      "version": 1,
      "tiles": [
        { "title": "Track 1" }
      ],
      "tileDrawer": [],
      "boardLayout": "auto-grid",
      "activeOptionsMenu": "play"
    },

    // Full local asset library. Backup import replaces the existing local assets.
    "assets": [
      {
        "id": "crown",
        "name": "Crown",
        "token": "crown",
        "kind": "image",
        "mime": "image/png",
        "data": "data:image/png;base64,..."
      }
    ]
  }
}
```

## Canonical Board Example

```jsonc
{
  "board": {
    "version": 2,
    "settings": {
      // Board layout. Accepted values:
      // "auto-grid", "set-grid", "frame", "slots", "cases".
      "layout": "auto-grid",

      // Gameplay mode. Auto grid, set grid, and frame use:
      // "flyover", "shuffle", "cascade".
      // Cases use: "right", "left".
      // Slots use: "down", "up".
      "mode": "flyover",

      // Used by "set-grid". If more tiles exist than fit, rows expand so tiles are not cut off.
      "grid": {
        "columns": 4,
        "rows": 4
      },

      // Used by "slots" and "cases". Odd values from 3 to 15 are exported.
      "visibleTiles": 7,

      // Board display options.
      "showStagger": true,
      "showTurnButton": true,

      // Board text display options. Missing fields fall back to defaults.
      // Front defaults preserve the compact board look: name + subtitle.
      // Back defaults preserve the full reveal look: name + pre-title + subtitle + extra.
      "tileDisplay": {
        "front": {
          "name": true,
          "preTitle": false,
          "subtitle": true,
          "extra": false
        },
        "back": {
          "name": true,
          "preTitle": true,
          "subtitle": true,
          "extra": true
        }
      },

      "syncWinnerFlipToBoard": false
    },
    "assets": [
      {
        // User-defined token used in tile text as :crown:.
        "token": "crown",

        // Display name in the Assets panel.
        "name": "Crown",

        // Current supported kind is "image". The shape leaves room for sounds later.
        "kind": "image",

        // Supported image mime types: image/png, image/jpeg, image/webp, image/gif.
        // The data URL MIME must match this value.
        "mime": "image/png",

        // Base64 data URL so exported boards and asset packs are self-contained.
        // Remote URLs are intentionally not imported.
        "data": "data:image/png;base64,..."
      }
    ],
    "tiles": [
      {
        // Tile type. Accepted values: "text" and "image".
        "type": "image",

        // Asset token used by image tiles. Write without surrounding colons in JSON.
        "imageToken": "crown",

        // Image tile cover offset from center, in percent.
        // 0 / 0 is centered; negative X moves left, positive X moves right;
        // negative Y moves up, positive Y moves down.
        "imageOffsetX": 0,
        "imageOffsetY": 0,
        "imageScaleOffset": 0,

        // If true, the image tile still uses its image in Winner Display but hides it on the board.
        "hideImageOnBoard": false,

        // Front Name
        "title": "Rainbow Road :crown:",

        // Front Pre-Title
        "preTitle": "Cup finale",

        // Front Sub-Title
        "subtitle": "Mirror mode",

        // Front Extra Info
        "detail": "Use this field for longer front-side notes.",

        // Back Name
        "backsideName": "Secret Route",

        // Back Pre-Title
        "backsideTitle": "Back",

        // Back Sub-Title
        "backsideSubtitle": "Hidden option",

        // Back Extra Info
        "backside": "Plain text shown on the back of the card.",

        // Front-side count repeat before selection can move past this tile.
        "stagger": 1,

        // Back-side count repeat. If omitted, it inherits the front stagger.
        "backsideStagger": 5,

        // Front status. Accepted values: "normal", "faded", "hidden".
        "status": "normal",

        // Back status. Accepted values: "normal", "faded", "hidden".
        "backsideStatus": "normal",

        // Front tile color.
        "base": "#151515",

        // Back tile color.
        "backsideBase": "#252525",

        // Selection highlight color.
        "highlight": "#f4f4f4",

        // Front text color.
        "textColor": "#f4f4f5",

        // Back text color.
        "backsideTextColor": "#f4f4f5",

        // Optional front-side text-type colors. If omitted, textColor is used.
        "nameTextColor": "#f4f4f5",
        "preTitleTextColor": "#d6d6d8",
        "subtitleTextColor": "#c8c8cc",
        "extraTextColor": "#eeeeef",

        // Optional back-side text-type colors. If omitted, backsideTextColor is used.
        "backsideNameTextColor": "#f4f4f5",
        "backsidePreTitleTextColor": "#d6d6d8",
        "backsideSubtitleTextColor": "#c8c8cc",
        "backsideExtraTextColor": "#eeeeef",

        // Text style.
        "font": "system",
        "weight": "760",
        "textAlign": "center",
        "titleScale": 1,
        "subtitleScale": 1,

        // Optional per-field Winner Display text overrides.
        // Missing fields inherit the tile/global text style.
        "winnerTextStyles": {
          "title": {
            "font": "serif",
            "weight": "850",
            "italic": false,
            "uppercase": false,
            "align": "center",
            "color": "#ffffff",
            "scale": 1.2
          }
        },

        // Whether the tile is showing its back side on the board.
        "flipped": false
      }
    ]
  }
}
```

## Tile Fields

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `type` | string | `text` | Tile type. Accepted values: `text`, `image`. |
| `imageToken` | string | `""` | Asset token for Image tiles. Use the token without colons, such as `crown`; text can still use `:crown:` inline tokens. If `imageToken` is present and `type` is omitted, import infers an Image tile. |
| `imageOffsetX` | number | `0` | Image tile horizontal cover offset in percent. Accepted range: `-100` to `100`; `0` is centered. |
| `imageOffsetY` | number | `0` | Image tile vertical cover offset in percent. Accepted range: `-100` to `100`; `0` is centered. |
| `imageScaleOffset` | number | `0` | Image tile scale offset in percent. Accepted range: `0` to `300`; `0` keeps the fitted image at its default cover size, `100` doubles the rendered image scale. |
| `hideImageOnBoard` | boolean | `false` | If `true`, an Image tile hides its fitted image on the board but still shows it in Winner Display. |
| `hideTextOnBoard` | boolean | `false` | If `true`, an Image tile hides its text on the board while keeping the full tile image visible behind the tile face. |
| `hideTextOnWinner` | boolean | `false` | If `true`, an Image tile hides its text in Winner Display while keeping the full-card image visible. Winner edit mode still reveals text for editing. |
| `title` | string | `""` | Front Name. Empty text is hidden in tile and winner displays. |
| `preTitle` | string | `""` | Front Pre-Title. Also used as the winner display label for the front side. |
| `subtitle` | string | `""` | Front Sub-Title. |
| `detail` | string | `""` | Front Extra Info. Supports newline text display. |
| `backsideName` | string | `""` | Back Name. |
| `backsideTitle` | string | `""` | Back Pre-Title. Also used as the winner display label for the back side. |
| `backsideSubtitle` | string | `""` | Back Sub-Title. |
| `backside` | string | `""` | Back Extra Info. Supports newline text display. |
| `stagger` | number | `1` | Front-side whole-number count repeat. Current import clamps to the app's accepted range of `1` to `99`. |
| `backsideStagger` | number | front stagger | Back-side whole-number count repeat. If omitted, it inherits `stagger`; if only the back stagger is provided, the front side inherits it. |
| `status` | string | `normal` | Front status. Accepted values: `normal`, `faded`, `hidden`. Faded greys the front side; hidden darkens it and replaces front text with an eye icon. |
| `backsideStatus` | string | `normal` | Back status. Accepted values: `normal`, `faded`, `hidden`. Faded greys the back side; hidden darkens it and replaces back text with an eye icon. |
| `base` | color string | app front base | Front tile color. Use hex like `#151515`. |
| `backsideBase` | color string | app back base | Back tile color. Uses front color fallback if omitted. |
| `highlight` | color string | app highlight | Selection highlight color. Use hex like `#f4f4f4`. |
| `textColor` | color string | app front text | Front-side fallback text color. Individual front text color fields override this when present. |
| `backsideTextColor` | color string | app back text | Back-side fallback text color. Individual back text color fields override this when present. |
| `nameTextColor` | color string | `textColor` | Front Name text color. |
| `preTitleTextColor` | color string | `textColor` | Front Pre-Title text color. |
| `subtitleTextColor` | color string | `textColor` | Front Sub-Title text color. |
| `extraTextColor` | color string | `textColor` | Front Extra Info text color. |
| `backsideNameTextColor` | color string | `backsideTextColor` | Back Name text color. |
| `backsidePreTitleTextColor` | color string | `backsideTextColor` | Back Pre-Title text color. |
| `backsideSubtitleTextColor` | color string | `backsideTextColor` | Back Sub-Title text color. |
| `backsideExtraTextColor` | color string | `backsideTextColor` | Back Extra Info text color. |
| `font` | string | `system` | Accepted values: `system`, `serif`, `mono`, `condensed`. |
| `weight` | string or number | `760` | Accepted values: `650`, `760`, `850`. |
| `textAlign` | string | `center` | Text orientation for tile and winner text. Accepted values: `left`, `center`, `right`. |
| `titleScale` | number | `1` | Relative front/back name size. Accepted range: `0.1` to `8`. |
| `subtitleScale` | number | `1` | Relative subtitle/detail size. Accepted range: `0.1` to `8`. |
| `winnerTextStyles` | object | omitted | Optional Winner Display-only text overrides keyed by field (`preTitle`, `title`, `subtitle`, `detail`, `backsideTitle`, `backsideName`, `backsideSubtitle`, `backside`). Each field can include `font`, `weight`, `italic`, `uppercase`, `align`, `color`, and `scale`; missing properties inherit from the tile/global style. |
| `flipped` | boolean | `false` | If `true`, the tile starts on its back side on the board. |

## Bulk Line Shape

Bulk mode is not JSON, but it maps to the same tile fields:

```text
Front Title|Subtitle || Back Title|Subtitle
@image :crown: Front Title|Subtitle || Back Title|Subtitle
```

- Each line creates one tile.
- Prefix a line with `@image :token:` to create an Image tile using an imported image asset.
- Bulk does not edit `hideImageOnBoard`, `hideTextOnBoard`, or `hideTextOnWinner`; use the Edit menu or JSON for those fields.
- Stagger prefixes still work with Image tiles, for example `@image :crown: <3> Front|Sub || <2> Back|Sub`.

## Board Settings

`board.settings` is optional. Missing settings do not block import; the app keeps its current defaults or current board options.

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `layout` | string | `auto-grid` | Accepted values: `auto-grid`, `set-grid`, `frame`, `slots`, `cases`. |
| `mode` | string | layout default | Auto grid, set grid, and frame allow `flyover`, `shuffle`, `cascade`; cases allow `right`, `left`; slots allow `down`, `up`. Invalid mode/layout combinations fall back to the layout default. |
| `grid.columns` | number | `4` | Set Grid width in tiles. Accepted import range is `1` to `12`. |
| `grid.rows` | number | `4` | Set Grid height in tiles. Accepted import range is `1` to `12`; rows can expand at render time so tiles are not cut off. |
| `visibleTiles` | number | `7` | Slots/Cases visible tile count. Accepted import range is odd values from `3` to `15`; even imports are normalized to the next odd value when possible. |
| `showStagger` | boolean | `true` | Shows or hides tile stagger numbers. |
| `showTurnButton` | boolean | `true` | Shows or hides the per-tile turn button. |
| `syncWinnerFlipToBoard` | boolean | `false` | Winner display flips can also flip the source board tile. |

## Accepted Aliases

The importer accepts several alternate field names so AI-generated or hand-written JSON does not need to be perfect.

| Canonical field | Accepted aliases |
| --- | --- |
| `type` | `tileType`, `contentType`, `front.type`, `back.type` |
| `imageToken` | `assetToken`, `imageAssetToken`, `image`, `front.imageToken`, `back.imageToken` |
| `imageOffsetX` | `imageX`, `imagePositionX`, `imageOffset.x` |
| `imageOffsetY` | `imageY`, `imagePositionY`, `imageOffset.y` |
| `imageScaleOffset` | `imageScaleOffsetPercent`, `imageZoomOffset`, `imageOffset.scaleOffset` |
| `hideImageOnBoard` | `hideBoardImage`, `imageHiddenOnBoard`, `hideImage` |
| `hideTextOnBoard` | `hideBoardText`, `textHiddenOnBoard`, `hideText` |
| `hideTextOnWinner` | `hideWinnerText`, `textHiddenOnWinner`, `hideTextInWinner` |
| `title` | `name`, `frontName`, `front.title`, `front.name` |
| `preTitle` | `pretitle`, `frontPreTitle`, `front.preTitle`, `front.pretitle` |
| `subtitle` | `subTitle`, `frontSubtitle`, `frontSubTitle`, `front.subtitle`, `front.subTitle` |
| `detail` | `extraInfo`, `frontExtraInfo`, `frontExtra`, `front.detail`, `front.extraInfo`, `front.extra` |
| `backsideName` | `backName`, `back.name`, `back.title` |
| `backsideTitle` | `backPreTitle`, `back.preTitle`, `back.pretitle` |
| `backsideSubtitle` | `backSubtitle`, `backSubTitle`, `back.subtitle`, `back.subTitle` |
| `backside` | `backDetail`, `backExtraInfo`, `backExtra`, `back.detail`, `back.extraInfo`, `back.extra`, `back.text` |
| `stagger` | `frontStagger`, `front.stagger` |
| `backsideStagger` | `backStagger`, `back.stagger` |
| `status` | `frontStatus`, `frontState`, `state`, `visibility`, `front.status`, `front.state`, `front.visibility` |
| `backsideStatus` | `backStatus`, `backState`, `backVisibility`, `back.status`, `back.state`, `back.visibility` |
| `base` | `frontBase`, `frontBaseColor`, `color`, `front.base`, `front.color` |
| `backsideBase` | `backBase`, `backBaseColor`, `back.base`, `back.color` |
| `highlight` | `highlightColor`, `accent` |
| `textColor` | `frontTextColor`, `fontColor`, `front.textColor`, `front.colorText` |
| `backsideTextColor` | `backTextColor`, `backFontColor`, `back.textColor`, `back.colorText` |
| `textAlign` | `align`, `alignment`, `front.textAlign`, `front.align`, `back.textAlign`, `back.align` |
| `flipped` | `isFlipped`, `showBack`, `side: "back"`, `face: "back"` |

Board settings aliases:

| Canonical field | Accepted aliases |
| --- | --- |
| `layout` | `boardLayout`, `type` |
| `mode` | `gameplayMode` |
| `grid.columns` | `columns`, `width`, `grid.width` |
| `grid.rows` | `rows`, `height`, `grid.height` |
| `visibleTiles` | `visible`, `slotVisibleTiles`, `caseVisibleTiles` |
| `showTurnButton` | `showTileFlipButton` |

`back` can also be an object containing back-side fields:

```jsonc
{
  "title": "Front name",
  "back": {
    "name": "Back name",
    "preTitle": "Back label",
    "subtitle": "Back subtitle",
    "text": "Back extra info",
    "base": "#303030",
    "textColor": "#f4f4f5"
  }
}
```

## Minimal Examples

### Empty Tile

```json
{}
```

### Title-Only Tile

```json
{
  "title": "Only a name"
}
```

### Partial Board

```json
{
  "board": {
    "tiles": [
      {
        "title": "Alpha"
      },
      {
        "subtitle": "No title, still imports"
      },
      {}
    ]
  }
}
```

## Versioning

Current board export version: `2`.

Future changes should keep imports backward-compatible when possible. If the exported structure changes in a breaking way, increment `board.version` and document the migration behavior here.
