project.json

The whole edit in one file: canvas, markers, tracks, media and clips.

{
  "name": "My Edit",
  "width": 1280, "height": 720, "fps": 30,   // composition canvas + timeline/export rate — sole FPS source (UI: FPS select)
  "exportFrame": { "x": 438, "y": 0, "w": 404, "h": 720 },  // optional delivery crop (even w/h for H.264)
  // ^ omit = export the full canvas. Preview dims outside this rect; Fast export crops
  // JPEGs to w×h. Clip x/y/scale stay relative to the composition center, not the frame.
  // Frame size is always even (yuv420p / libx264); 9:16 on 1280×720 → 404×720, not 405.
  "background": "#000000",                    // canvas color behind all clips (optional)
  "revision": 7,                              // bump on every write!
  "panSchema": 1,                             // 1 = pan-aware; omit only on pre-pan projects (UI migrates once)
  "markers": [ { "t": 2.5 }, { "t": 5.0, "label": "drop", "color": "red" } ],
  // ^ beat/cue markers: diamonds on the ruler (label shown beside them), snap
  //   targets for clip edges. color: gold (default) · red · orange · green ·
  //   cyan · blue · purple · pink. Kept sorted by t; the UI rounds t to 1 ms.
  "inPoint": 10.023, // IN work-area marker (Limit playback + optional export range)
  "outPoint": 21.500, // OUT work-area marker; omit = timeline end
  "folders": [
    // Project-bin virtual folders (tree). Media reference them via folderId.
    { "id": "f_broll", "name": "B-roll", "parentId": null, "open": true }
  ],
  "disabledTracks": [ "A2" ],
  // ^ optional — track ids (e.g. V4 V3 V2 V1 A1 A2 A3) omitted from preview/export when listed
  "lockedTracks": [ "A3" ],
  // ^ optional — locked lanes: nothing on them may be edited or moved (see Semantics)
  "untargetedTracks": [ "V2" ],
  // ^ optional — lanes edits skip (split, insert, ripple, close gap…); default: all targeted
  "encodeProfile": "hq",  // optional — fast-export profile id (see encoding-profiles.json)
  "tracks": [                          // optional — timeline lanes (default V3…V1 + A1…A4)
    { "id": "V3", "kind": "video" },   // video: higher number drawn on top (V1 under V2 under V3…)
    { "id": "V2", "kind": "video" },
    { "id": "V1", "kind": "video" },
    { "id": "A1", "kind": "audio" },   // audio: A1…An top→bottom; +V/+A in the UI appends Vn+1 / An+1
    { "id": "A2", "kind": "audio" },
    { "id": "A3", "kind": "audio" },
    { "id": "A4", "kind": "audio" }
  ],
  "media": [
    { "id": "m_abc", "name": "intro.mp4", "kind": "video",  // video|audio|image|svg
      "src": "/media/intro.mp4",             // path under ./media or ./library (never a raw https:// URL)
      "duration": 12.4, "width": 1920, "height": 1080,
      "folderId": null }                     // optional: id of a folders[] entry
  ],
  "clips": [
    {
      "id": "c_xyz",             // unique string
      "mediaId": "m_abc",        // null for kind:"text" and kind:"adjust"
      "kind": "video",           // video | audio | image | svg | text | adjust
      "track": "V1",             // Vn…V1 (video) | A1…An (audio); extras via UI +V/+A or tracks[]
      "start": 0,                // timeline position, seconds
      "in": 2.5,                 // offset into source media, seconds (0 for image/svg/text)
      "duration": 5,             // clip length on timeline, seconds
      "name": "intro",
      "linkGroup": "lg_abc",     // OPTIONAL — AV link: video + per-channel audio stems share one id
      "locked": true,            // OPTIONAL — locked by the user: leave it alone (MCP patch refuses edits)
      "disabled": true,          // OPTIONAL — stays on the timeline, hidden from preview / audio / export
      "unlinked": true,          // OPTIONAL — the user unlinked this clip; never auto-relinked on load
      "props": { /* all optional — see the props reference below */ },
      // OPTIONAL — keyframe animation. Times are seconds RELATIVE TO CLIP START.
      // "ease" sits on the DESTINATION keyframe of each segment:
      // linear | ease-in | ease-out | ease-in-out (default ease-in-out).
      "keyframes": {
        "scale":   [ { "t": 0, "v": 1 }, { "t": 5, "v": 1.25, "ease": "linear" } ],
        "opacity": [ { "t": 0, "v": 0 }, { "t": 0.8, "v": 1 } ]
      },
      // OPTIONAL — transitions at the clip's head/tail.
      "transitionIn":  { "type": "fade", "duration": 0.8 },
      "transitionOut": { "type": "slide-left", "duration": 0.6 }
    }
  ]
}