KeynoteKit · Tutorial

Build Keynote decks
from a JSON file.

KeynoteKit turns a plain JSON spec into a real .key file — full-bleed photos, gradients, shapes, colored bullets, and per-bullet build animations, on a 16:9 canvas. Author slides three ways: from scratch, from a reusable inline template, or from a Keynote template deck.

$ iwatool build-json deck.json out.key
You write
deck.json
→
KeynoteKit
build-json
→
You open
out.key
01

Set up a Keynote template

Optional — only needed for slides that clone a real Keynote layout (Way 3 below). A template is just a .key whose slides each demonstrate one layout, so KeynoteKit can clone the right one and inherit its masters and styling.

terminal
# Explode an installed theme into a tagged template
scripts/make-template.sh "Basic Black" brand.key

# ...or tag slides yourself, in each slide's presenter notes:
#   @layout: title
#   @layout: bullets
#   @layout: statement

# See what a template exposes
iwatool describe-template brand.key
02

The JSON file

A deck is a list of slides. Each element is placed by frame on a 1920×1080 canvas. Colors are hex or [r,g,b,a]; frames are explicit or a cover/fit helper that reads the image's real aspect ratio.

deck.json
{
  "$schema": "./deck.schema.json",
  "defaultFont": "Helvetica Neue",
  "imageBaseDir": "assets",
  "template": "brand.key",          // optional — for Keynote-template slides
  "templates": { },                 // optional — reusable inline layouts
  "slides": [
    {
      "background": "#0E2529",
      "transition": { "effect": "apple:push", "duration": 0.4, "direction": "fromBottom" },
      "elements": [ ]
    }
  ]
}
Colors

Hex or array

"#0E2529" or [0.05, 0.14, 0.16, 1]. A bare color is a fill anywhere.

Frames

Explicit or fit

{x,y,width,height}, or cover / fit / coverBox that measure the image.

Fonts

Family or face

A deck defaultFont, overridable per element; bold / italic apply as traits.

Nested bullets: prefix a line in a bulleted element's text with tab characters — one per level — e.g. "Otters\n\tRiver\n\t\tSea". Keynote stores the level per paragraph; the tabs are stripped.

03

Three ways to build a slide

Mix and match freely — all three can live in one deck. When you set a deck-level template, from slides clone its layouts while free-form and use slides are synthesized onto a blank slide alongside them.

1 From scratch
a free-form slide
{
  "background": "#0E2529",
  "elements": [
    {
      "type": "shape",
      "shape": { "kind": "roundedRectangle", "cornerRadius": 16 },
      "frame": { "x": 160, "y": 300, "width": 90, "height": 16 },
      "fill": "#4CD9C2"
    },
    {
      "type": "text",
      "text": "Built from scratch",
      "frame": { "x": 160, "y": 340, "width": 1500, "height": 220 },
      "font": "Futura",
      "fontSize": 150,
      "bold": true,
      "color": "#EDF7F5"
    },
    {
      "type": "text",
      "text": "Every element placed by absolute frame.",
      "frame": { "x": 168, "y": 560, "width": 1400, "height": 80 },
      "fontSize": 44,
      "italic": true,
      "color": "#8CB3B2"
    }
  ]
}
out.key · slide 1 rendered in Keynote
A slide built from scratch: a coral accent bar, a large 'Built from scratch' heading, and an italic subtitle on a deep teal background.
2 Inline reusable template
a template + a slide that uses it
"templates": {
  "content": {
    "elements": [
      { "type": "shape", "frame": { … }, "fill": "#4CD9C2" },
      {
        "type": "text", "name": "kicker", "text": "",
        "fontSize": 30, "bold": true, "color": "#4CD9C2"
      },
      {
        "type": "text", "name": "title", "text": "",
        "font": "Futura", "fontSize": 96, "bold": true
      }
    ]
  }
},

"slides": [
  {
    "use": "content",
    "set": {
      "kicker": { "text": "REUSABLE LAYOUT" },
      "title":  { "text": "Filled from a template" }
    },
    "elements": [
      {
        "type": "text", "name": "body",
        "text": "Define the layout once\nInstantiate it per slide\nFill slots by name",
        "bulleted": { "marker": "•", "color": "#4CD9C2" }
      }
    ]
  }
]
out.key · slide 1 rendered in Keynote
A slide from an inline template: an aqua kicker 'REUSABLE LAYOUT', a big 'Filled from a template' title, and three aqua bullets.
3 From a Keynote template
clone a layout, fill it
{
  "template": "brand.key",
  "slides": [
    {
      "from": { "layout": "bullets" },
      "set": {
        "title": "From a Keynote template",
        "body":  "Clone a real layout\nInherit its masters & styles\nFill the placeholders"
      },
      "override": [
        { "target": "Hero Photo", "image": "team.jpg" }
      ]
    }
  ]
}
out.key · slide 1 rendered in Keynote
A slide cloned from a Keynote template's bullets layout, filled with the title 'From a Keynote template' and three bullets, styled by the template's own theme.
04

Build it — and fix it fast

One command turns the spec into a deck. Validation is fail-fast and exhaustive: an invalid spec reports every problem at once, each with a path, and writes nothing — so an LLM (or you) can fix them all in one pass.

terminal
$ iwatool build-json deck.json out.key
built deck from JSON: out.key
a spec with mistakes
invalid deck spec:
  • slides[0].elements[0]: missing frame
  • slides[0].elements[1]: unknown alignment "middle"
  • slides[0].elements[2]: image not found: hero.jpg
  • slides[0].builds[0]: target "ghost" is not a named
    element on this slide

The full grammar — with a description on every field — lives in Examples/deck.schema.json. Point an LLM at it and ask for a deck.