Skip to content

Project specification

A plain-text format for describing a film.

Version 1.5 — Draft
Date 10 September 2026
Format version "filmopen": 1
Status Public draft. Open for comment.
Licence This specification: CC-BY-4.0. The JSON Schemas derived from it: CC0.
Canonical URL https://filmopen.ai/spec
Schemas https://filmopen.ai/schema/1/

A FilmOpen project is a folder of plain JSON files that describes a feature film, series or short completely: its characters, places, props and looks; its script; its shots and sound; and the media that realises them. The format is designed to be read and written by people, by scripts, and by AI systems; to reduce file conflicts when many contributors work on one film; and to remain fully meaningful as a bare folder of files with no software present.

It serves two directions equally. Forward: write a film in text and generate it with AI models. Reverse: analyse an existing film and describe it in text, so that it can be studied, adapted, translated or regenerated.


  • Kind. kind is now one of short · feature · miniseries · series (§8.1). Readers read the 1.4 words as their nearest value: film as feature, franchise as series.
  • Every project declares its epochs, and every library entity has one. epochs is required in the project file with at least one entry; the first in order is the project’s default epoch, the one an implicit reference falls back to (§6.1, §8.2). An epoch may carry a year. A character may carry a birthYear; a reader proposes appearance.age at each epoch from the two and the author overrides it (§9.1). The reserved token default remains valid as an epoch name.
  • Version label. The common header gains an optional label: a short word for the version in version lists (§7). It does not travel with a fork.
  • The creator directs. A writer that creates a project lists its creator in contributors with role: "director" (§8.3, §18.3).
  • Pick. The version an author marks with prefer in their commentary is their pick; an application shows it beside official and lets the author change it (§13.4). Removing an official pointer is done by deleting the pointer file (§13.2).
  • Project manifest. A project folder is identified by one file with a fixed name at its root, filmopen-project.json (§4.4). It holds the settings that belong to the folder rather than to any author’s version of the film: the project tag and where the rendered media lives. It is not versioned, not authored and not creative content; it is the one file an application writes when it creates a project.
  • Media root. Rendered and imported media may live in a separate folder from the JSON — a folder synced by a different service, or a folder outside version control. The manifest names it with a typed locator (§4.4); takes are discovered there (§12.1) and media references resolve against it (§6.3). Absent a manifest, the project folder is its own media root, as in 1.3.
  • Shared library location. Models, platforms and plugins are the application’s shared library (§14.5), installed and updated independently of any project and of the application itself. A project folder need not contain them; a project may still hold its own.
  • Authored projects. pj_<tag>_<author>_v<n>.json uses the same author, version and forkedFrom conventions as other spec entities. A remake can change language, title, delivery settings and story roots in its own complete project file (§4.1, §8.6).
  • Project selection. Opening an explicit project file selects it. Folder opening uses the project official pointer, a sole author’s highest version, or an explicit user selection. The selected project supplies the context for reference resolution and export.
  • Portable references. Grouped reference media, epoch choices, plain paths and URLs remain supported. Story-root and child lists accept tags or full entity references.
  • Consistent examples. The worked example has a resolvable style, a declared import batch, explicit ambience trim and frame-aligned timings. Provider definitions are identified as outside that example.
  • Plugin contracts. Validation inputs and response timing are clarified. Code trust is an application decision; an official pointer is a creative selection, not permission to execute changed code.
  • Editor handoff. Final Cut Pro 7 XML is distinguished from FCPXML; relinking and export guarantees are limited to supported, tested behavior.

The format identifier remains "filmopen": 1 during draft development. This is a revised draft, not a promise that all previous draft projects validate unchanged. To update a 1.0/1.2 project, give its pj_<tag>.json an author and version in the filename and header, update any explicit references to it, and add an ordered story-root list (an empty list is valid for a blank project). No history records or migration database are required. Grouped refs and fractional frame rates added in 1.2 remain optional.


  1. Introduction
  2. Design principles
  3. Concepts and terminology
  4. Project structure
  5. Naming
  6. References and resolution
  7. Common header
  8. The project file
  9. Library entities
  10. Story structure and script
  11. Shots and cues
  12. Takes and render batches
  13. Versions, official, compare and commentary
  14. Extensibility: hooks, plugins, workspaces, shared library
  15. Timeline export and round-trip
  16. Interchange with existing standards
  17. Editing by hand or by machine
  18. Conformance
  19. Not in this version

Appendix A — Attribute vocabulary Appendix B — Worked example Appendix C — MovieLabs OMC correspondence


Film has mature interchange standards for the script (Fountain, FDX) and for the finish (ASC CDL, LUTs, OpenTimelineIO, EDL, FCPXML). It has nothing for the structured middle — the description of characters, places, props, looks and shots that an AI production pipeline needs, and that a collaborative team needs to share. This specification fills that gap.

This document defines:

  • the folder and file layout of a project;
  • the filename grammar that makes every file self-describing;
  • the JSON content of each file type, with a small required header and a large optional vocabulary;
  • how files refer to one another and how references resolve;
  • how versions, forks, official selections and commentary work;
  • how rendered and imported media is recorded;
  • how the project maps to a timeline for an editing application;
  • how the format extends through hooks and plugins;
  • how it maps to and from existing standards.

It does not define any application, user interface, storage service, or rendering model. Application conveniences — caches, indexes, autosave, job queues — never become mandatory project files.

  • Filmmakers, who may use an application or read and edit the files themselves, and must never be required to do the latter.
  • Developers, who should be able to understand the entire format in an afternoon.
  • Automated systems — scripts, pipelines and AI agents — that read and write projects directly.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in RFC 2119. A reader is any software or person consuming a project. A writer is any software or person producing or modifying one.

Requirements define syntax and how a reader interprets data. They cannot stop anyone editing a file. Authorship and review conventions are human governance, not security features (§2, principle 8).

All examples are illustrative; some omit optional content. Field names in monospace are normative; the values shown are not. Model and platform names in examples do not promise any provider’s current capabilities.


  1. Plain JSON, complete documents. Every file fully describes one thing. There are no deltas, overlays, inheritance, reserved key prefixes, canonical serialisation rules, or content-hash addressing of JSON. Any JSON tool works.
  2. One writer per file. The author’s handle is in the filename, and by convention nobody else edits that file. This reduces shared-folder conflicts and makes comparison meaningful. A delegated edit to someone else’s file is still a valid file; a reader warns, and the maintainer decides.
  3. Self-describing filenames. A filename states what a file is, whose it is, and which version. Directories organise; they carry no meaning.
  4. Fork is copy. Versions are whole files: v1, v2, v3. Forking copies a file into your name. Files are edited in place; a new version is a deliberate act, not a side effect of saving.
  5. Official is a pointer. A small _official.json file names the version the project has chosen. It neither locks that file nor authenticates its author.
  6. Reference within, copy across. Inside one author’s work, files point at each other. Across authors, content is copied. Media is always referenced, never duplicated.
  7. Media sits beside the JSON, named so that any generated file traces back to its source. Imported media may keep its own name and is referenced by path.
  8. Warn, don’t enforce. Every file is editable text. Conventions are checked by readers and reported; nothing is locked. Whoever gatekeeps a shared repository makes the final call.

Git is optional. A project is a folder — on a disk, on a phone, or in a synced Drive or Dropbox folder. Principles 2 and 3 reduce conflicts; simultaneous edits can still conflict. Git is layered on when a team wants history or pull requests; history belongs to Git, and the format does not duplicate it.


Term Meaning
project A folder holding a film, series or franchise and its alternatives. An authored pj_ file at its root describes one complete project version.
author A handle naming the writer of a file. A person (maria) or a workspace or plugin run within a person’s identity (maria.locale-th).
owner The person behind an author handle: its first segment.
entity Anything described by one JSON file: character, location, scene, shot, model, …
library entity A reusable thing with an epoch axis: character, outfit, location, prop, style, misc, document.
epoch How a library entity appears at a point in the story: 30yo, pre-war, 1990s, wrecked. Declared in the project file, at least one; the first is the default epoch. Each epoch of an entity is a separate, complete file that shares the entity’s tag.
story unit A level of the script: season or installment, episode or act, sequence, scene.
block One unit of script inside a scene: a line of dialogue, an action, a title.
shot A visual generation spec covering one or more blocks.
cue An audio generation spec: dialogue, narration, music, sound effect, ambience.
take One rendered or imported output of a shot, cue or library entity. A media file.
render batch The record of how a set of takes was produced, imported, or generated by a plugin.
media reference A take stem, a project-relative path, or a URL (§6.3).
version A whole file, v1, v2, … The highest is the author’s working copy.
official The version the project has chosen for an entity, named by a pointer file.
prefer, pick A version one author would choose, recorded in their commentary. An opinion, not a decision.
picks Inside an entity, which take(s) its author chose.
commentary An author’s likes, ratings, comments and preferences about an entity.
placement Where a shot or cue sits on the timeline: track, anchor, offset, trim.
hook A short JavaScript function inside a model, platform, style or project file.
plugin A packaged JavaScript extension that transforms, analyses, imports or exports.
workspace A derived author handle (john123.wild) that keeps a body of work separate from the owner’s main versions. Plugin runs are workspaces. A filename convention, nothing more.

A project folder contains one project tag and one or more pj_<tag>_<author>_v<n>.json files at its root. They are complete alternatives of that project, with an optional pj_<tag>_official.json pointer. Other entity files may be organised in subfolders. External media may be referenced by URL.

Opening an explicit project file selects that version. When opening only a folder, a reader uses its project official pointer if present; otherwise it uses the highest version if there is exactly one project author handle. If several handles have project files, the reader asks which one to open (or a command-line caller supplies the filename), rather than guessing. An invalid official pointer is reported. The selected project supplies contributors, defaults, language, tracks and story roots for that view or export. Remembering that selection is an application preference, not a required film file.

A separate film with another project tag belongs in its own folder. A full entity stem is unique within the project; readers report duplicates rather than choosing the first file found.

Creative content and decisions live in JSON. Images, audio, video, fonts and LUTs are external files beside it. Plugin code is JavaScript. None of these is embedded into JSON to make the project “text”.

Folder Purpose
render/ Render batch records, and emitted workflows for offline rendering.
conform/ Stable-name media slots for the editing application (§15). Generated; may be deleted and regenerated.
_inbox/ Where people put material awaiting assignment.

All other folder names are free. A reader MUST NOT move, rename or delete files it does not understand; it reports them. Application caches, thumbnails and indexes may live wherever the application chooses and are never the only copy of creative content.

Where the folder lives Offline Collaboration
Local disk, desktop or phone yes no
Drive / Dropbox / iCloud with offline sync yes yes — shared folder
Local folder that syncs to a cloud folder yes yes — also the desktop-to-phone path
Git repository yes yes — with history and review

Because every file has one writer, a shared sync folder rarely sees a genuine conflict. The only files with a shared-write role are _official.json files, normally written by directors (§8.3). Project versions themselves follow the same author convention as other entities.

The remaining case is the same author editing the same file on two devices while both are offline. Sync clients surface this as a sibling file (… (conflicted copy), … (1)). A reader SHOULD recognise such a sibling of its author’s own file and present the two for comparison (§13.3); it MUST NOT delete either silently.

Large media may live in the folder, in Git LFS, or in external storage reached by URL (§6.3). A clone holding only LFS pointers does not yet hold the video; a reader shows unavailable media clearly and lets the user fetch or relink. Keep API keys and machine-specific paths out of committed project data.

4.4 The project manifest and the media root

Section titled “4.4 The project manifest and the media root”

Every other file in a project is named by its author and version, because it is somebody’s creative work. One file is not: the file that says “this folder is a FilmOpen project” and records the settings that belong to the folder itself. It has a fixed name so that any reader can recognise a project without scanning, and so that a project folder — which normally carries the film’s short name — is identified by its content and not by its name:

filmopen-project.json
{
"filmopen": 1,
"tag": "cartographer",
"data": { "type": "local", "path": "../the-cartographer-data" }
}
Field Required Meaning
filmopen yes The format version, as in every header (§7).
tag yes The project tag. The folder’s pj_<tag>_… files carry the same tag; a reader reports a disagreement.
data no Where rendered and imported media lives — the media root. Absent, the project folder is its own media root.
library no Reserved: a locator for the shared library the project was seeded from (§14.5). Readers ignore it in this version.
note no Free text.

The manifest is not an entity: it has no author, no version and no official pointer. It is shared-write in the way _official.json files are (§4.3) and changes rarely — normally only when the project is created or its media is moved. It MUST NOT hold credentials, API keys or anything specific to one machine (§4.3); a locator names where, never how to log in.

Locators. data is an object, not a path string, because the media may live somewhere that is not a folder on this machine:

type path means Reader support in this version
local a folder path with / separators, relative to the project folder (../the-cartographer-data, media) or absolute MUST support. Relative paths are portable and are what a writer SHOULD write; an absolute path is machine-specific and is reported.
dropbox a path inside the user’s Dropbox MAY support; otherwise report the media root as unavailable.
google-drive a path inside the user’s Google Drive as above
onedrive a path inside the user’s OneDrive as above
icloud a path inside iCloud Drive as above
url a base URL; media references are appended to it MAY support

A reader that does not support a locator type MUST still open the project: the JSON is complete without the media, and unavailable media is shown as unavailable (§4.3), never as an error that hides the film. An application MAY remember a per-machine substitute for a locator it cannot reach; that memory is an application preference, not part of the project.

A project without a manifest is still a project (readers SHOULD warn, and continue, treating the folder as its own media root), so that a folder hand-assembled from files, or one written by a 1.3 writer, opens. A writer that creates a project MUST write the manifest.

Why a separate media root. The JSON of a whole feature is a few megabytes and diffs cleanly; its renders are gigabytes and do not. Keeping them apart lets the JSON live in Git while the media lives in a synced folder, and lets a collaborator clone the film’s text without its pictures. The media root is a plain folder: files in it are named exactly as they would be inside the project folder (§5.4), so moving media between the two changes nothing but the manifest.


Filenames are lowercase ASCII. macOS and Windows filesystems are case-insensitive by default; r8Af0 and r8af0 are one file there and two files on Linux. Lowercase everywhere removes that class of bug.

Token Rule Examples
type two lowercase letters (§5.3) ch lo sc
segment [a-z0-9][a-z0-9-]{0,19} — at most 20 characters; readers SHOULD warn above 10 main-hero cafe seedance25
tag one or more segments joined by .; a dot expresses containment main-hero main-hero.suit 5.1 e2.1
epoch one segment; reserved default 30yo pre-war 1990s
author <owner>[.<workspace>]; each a segment; reserved owner official john123 maria.locale-th paul.wild
v<n> version, integer ≥ 1 v1 v2
r<id> render batch: r + a segment, unique per author. Generated ids are four lowercase hex digits; people may name batches r8af0 rstage0
<n> take number for one source and deliverable within a batch, integer ≥ 1 1 2

The 20-character limit applies to every segment. Readers SHOULD report filesystem path-length limits before writing rather than shorten names. Tags are unique within a type; the authors, epochs and versions of one entity deliberately share its tag.

Prefix Entity Grammar Epoch
pj project spec; versions at the project root
ch character library yes
of outfit library; tag <character>.<outfit> yes
lo location library; sub-areas as <location>.<area> yes
pr prop library yes
st style library yes
mi misc — fonts, logos, title cards, licensed audio, reference files library yes
dc document — treatment, series bible, pitch, research library yes
mo model — a generative or analysis model spec
pl platform — where models run spec
pg plugin manifest spec
se season / installment spec
ep episode / act / arc spec
sq sequence spec
sc scene — contains the blocks spec
sh shot; tag <scene>.<n> spec
cu cue; tag <unit>.<n> where unit is a scene, sequence or episode spec
cm commentary cm_<type>_<tag>_<author>.json
rd render batch rd_<id>_<author>.json
library entity <type>_<tag>_<epoch>_<author>_v<n>.json
spec entity <type>_<tag>_<author>_v<n>.json
official pointer <type>_<tag>[_<epoch>]_official.json
commentary cm_<type>_<tag>_<author>.json
render batch rd_<id>_<author>.json
take (output) <deliverable>_<source stem>_r<id>_<renderer>_<n>.<ext>

A parser splits on _, reads the first token, and dispatches. A take’s filename embeds the entire stem of its source file and the batch and renderer that produced it, so a generated file traces to its origin and to rd_<id>_<renderer>.json with no lookup. Imported media may instead keep its own name and be referenced by path (§6.3).

Prefix Deliverable From
cs concept sheet — character, location, prop, outfit, style board ch lo pr of st
sb storyboard frame — sketch-quality sh
ff first frame sh
lf last frame sh
kf keyframe — an intermediate frame sh
cl clip — video, with or without sound sh
vo voice — dialogue, narration cu
mu music cu
sx sound effect cu
am ambience, room tone cu

An author handle may carry a second segment after a dot: john123.locale-th. The owner is the first segment. Derived handles are used for:

  • Plugin runs. A plugin invoked by john123 writes under john123.<plugin>[-<qualifier>], e.g. john123.locale-th. Everything one run produces starts at v1; a second run is v2. A run is therefore one glob: *_john123.locale-th_v1.*. The batch record lists the files (§12.4).
  • Personal workspaces. john123.wild keeps an experiment from inflating the owner’s main version numbers.

A reader treats a derived handle as a distinct author for versioning and as the owner’s for warnings. Derived handles are not listed in the project file.

pj_cartographer_john123_v1.json John's project version
pj_cartographer_suda_v1.json Suda's remake; its own language and story roots
pj_cartographer_official.json → the chosen project version
ch_main-hero_30yo_john123_v2.json John's second version of the 30-year-old main hero
ch_main-hero_30yo_maria_v1.json Maria's fork of it
ch_main-hero_30yo_official.json → names one of the above
ch_main-hero_50yo_maria_v1.json a different epoch: a distinct file sharing the tag
of_main-hero.suit_30yo_john123_v1.json an outfit belonging to main-hero
lo_cafe_1990s_john123_v1.json the cafe as it looked in the 1990s
lo_cafe.kitchen_1990s_john123_v1.json a sub-area of the cafe
pr_car_wrecked_susan_v1.json the car, wrecked
st_war-grade_default_maria_v1.json a look
dc_bible_default_john123_v3.json the series bible
mo_seedance25_paul_v3.json a model, from the shared library
pl_fal_paul_v1.json a platform
pg_locale_paul_v2.json a plugin manifest; its code is pg_locale_paul_v2.js beside it
se_s1_john123_v1.json season 1
ep_e2_john123_v1.json episode 2
sq_e2.arrival_john123_v1.json a sequence within episode 2
sc_5_john123_v1.json scene 5 and its blocks
sc_5_john123.locale-th_v1.json scene 5 as produced by a localisation plugin John ran
sh_5.1_john123_v1.json shot 1 of scene 5
cu_5.1_john123_v1.json cue 1 of scene 5
cu_e2.1_maria_v1.json a cue spanning episode 2 — score
cm_ch_main-hero_maria.json Maria's commentary on main-hero
rd_8af0_maria.json Maria's render batch 8af0
cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1.png concept sheet, take 1, Maria's render of John's v2
ff_sh_5.1_john123_v1_r8af0_maria_2.png first frame, take 2, of shot 5.1
cl_sh_5.1_john123_v1_r8af0_maria_1.mp4 clip, take 1
vo_cu_5.1_john123_v1_r8af0_maria_1.wav voice, take 1
ff_sh_5.1_john123_v1_r0001_john123_1.png a frame John extracted from an existing film
references/kira/front.png an imported reference, kept under its own name

Entities point at one another constantly. Three forms exist: short references and full references name entities; media references name files.

A tag: "location": "cafe". The field determines the type. Resolved by the reader in three steps.

Step 1 — Epoch. Use the explicit epoch on the referencing entry if present; else the enclosing scene’s epoch; else the enclosing unit’s; else default. An outfit takes its character’s resolved epoch unless one is given. If the epoch was chosen implicitly and the entity has no file for it, fall back to default and show that fallback. If the epoch was requested explicitly and does not exist, the reference is unresolved: report it; never substitute a different age or state silently.

Step 2 — Author. Depends on the referencing file’s status:

A file that is not named by an _official.json resolves author-first: the referencing file’s author’s highest version at that epoch; then, for a derived handle, the owner’s; then official.

A file that is named by an _official.json resolves through official only.

In plain words: a proposal sees its author’s world; official sees the official world. Maria’s fork of a scene previews with Maria’s fork of the cafe. A localisation plugin’s output is self-consistent the moment it is written. Once the director makes Maria’s scene official, it stops picking up her later experiments.

Step 3 — Fallback. If nothing is official and the selected project has a single contributor owner, use that owner’s highest version. Otherwise the reader MUST report an unresolved reference rather than guess.

Project-root references use the selected pj file as the referencing file, with its own author and official status. Subsequent references use the file containing them. The selected project remains the context for defaults and contributors; a full reference bypasses lookup.

This is a lookup over the current folder. Changing an official pointer or editing a referenced file changes what the project uses; readers SHOULD show the resulting selections when previewing a promotion. Historical states come from Git.

A file stem: "scene": "sc_5_john123_v1". Exact; no resolution. It names an editable file, not frozen content.

One of:

  • a take stemcl_sh_5.1_john123_v1_r8af0_maria_1 — resolved to the unique file with that basename, or to a batch item naming it;
  • a media path including extension — references/kira/front.png — using /, relative to the project’s media root (§4.4: the folder the manifest’s data names, else the project folder itself), never to the referencing file;
  • an external URLhttps://… — naming bytes outside the folder. Expiring signed URLs and credentials are not locators.

Generated takes carry their source, batch and renderer in the name. Imported and hand-supplied media keep their own names and are referenced by path; a batch item (§12) may describe them. A plain reference to a PNG or WAV is sufficient; no registry entry is required.

  • Project story roots and story-unit child lists accept tags or full references; the list key determines the type (seasonsse, episodesep, sequencessq, scenessc).
  • Story units, scenes, shots and cues use short references to library entities. A scene’s props may be tags or { "prop": "car", "epoch": "wrecked" }; a scene may set locationEpoch.
  • A shot or cue MAY carry a full reference to the scene version its block ids belong to. Absent, it means the resolved scene of that tag.
  • forkedFrom, prefer and on use full references or, for on, media references.
  • picks, refs, preview, lut, file and deliverable inputs use media references.

Every entity file begins with the same fields. They repeat the filename deliberately: the file stays self-describing if renamed, and a reader can flag disagreement. Project versions use this common header without an epoch. Official pointers, commentary and render batches use the headers shown in their own sections.

{
"filmopen": 1,
"type": "ch",
"tag": "main-hero",
"epoch": "30yo",
"author": "john123",
"v": 2,
"name": "Kira Voss",
"forkedFrom": "ch_main-hero_30yo_john123_v1",
"created": "2026-09-05T18:04:11Z",
"updated": "2026-09-06T09:40:00Z"
}
Field Required Meaning
filmopen yes format version, integer
type tag author v yes as in the filename
epoch library entities as in the filename
name yes display name, any language
label no a short word for this version in version lists — goth, classic. Not copied by a fork
created updated no RFC 3339 UTC. Informational; never used for precedence
forkedFrom no stem this file was copied from. A note; nothing resolves through it
by no plugin stem, when a plugin produced this version
source no origin of imported material: { "batch": "rd_0001_john123" }
license no SPDX identifier or CC URL for this entity’s content, if it differs from the project’s
notes no free text for people; excluded from prompts unless a user explicitly includes it
x no an object for application-specific data. Readers preserve it; the format ignores it

{
"filmopen": 1, "type": "pj", "tag": "cartographer", "author": "john123", "v": 1,
"name": "The Cartographer",
"kind": "series",
"genre": ["drama", "mystery"],
"year": 2026,
"logline": "A mapmaker discovers her charts are rewriting the coastline.",
"synopsis": "",
"lang": "pt-BR",
"rating": "12",
"format": { "aspect": "2.39:1", "fps": 24, "resolution": "1920x804", "color": "rec709" },
"runtimeMin": 45,
"credits": [
{ "role": "writer", "name": "João Silva", "handle": "john123" },
{ "role": "director", "name": "João Silva", "handle": "john123" }
],
"epochs": {
"default": { "label": "Present day", "order": 1, "year": 2026 },
"pre-war": { "label": "Before the war", "order": 2, "year": 1938 },
"mid-war": { "label": "During the war", "order": 3, "year": 1943 },
"post-war": { "label": "After the war", "order": 4, "year": 1947 }
},
"contributors": {
"john123": { "name": "João Silva", "role": "director" },
"maria": { "name": "Maria Costa" },
"susan": { "name": "Susan Park" }
},
"tracks": [
{ "id": "pic", "kind": "av", "name": "Picture" },
{ "id": "pic2", "kind": "av", "name": "Picture 2" },
{ "id": "dx", "kind": "audio", "name": "Dialogue" },
{ "id": "vo", "kind": "audio", "name": "Narration" },
{ "id": "sfx", "kind": "audio", "name": "SFX" },
{ "id": "amb", "kind": "audio", "name": "Ambience" },
{ "id": "mx", "kind": "audio", "name": "Music" }
],
"seasons": ["s1"],
"license": "CC-BY-SA-4.0",
"hooks": { "validate": "(ctx) => ctx.entity.type !== 'sc' || ctx.entity.blocks.length > 0 || 'empty scene'" }
}

kindshort · feature · miniseries · series. It sets the labels of the story levels (§10.1) — a mini-series and a series have seasons and episodes, a short and a feature have installments and acts — and the story-root list an application starts a blank project with (scenes, sequences, episodes, seasons respectively). Nothing else depends on it. Readers treat the 1.4 values film as feature and franchise as series.

Every project declares its epochs here, at least one, each with an optional label, order and year. Readers order them by order (an entry without one comes first, in the file’s order); a writer that reorders them renumbers order from 1. The first epoch is the project’s default epoch: the one an implicit reference resolves to (§6.1) and the one a new library entity is created at. The reserved token default may be used as an epoch name and is what a new project starts with.

Every library entity has an epoch — there is no “base” file outside the epoch axis. An epoch variant is a separate, complete file that shares the entity’s tag and is treated as a distinct entity: nothing propagates between epochs. A scene names an epoch and every library entity in it resolves to the matching file (§6.1). Comparing two epochs side by side and copying attributes across (§13.3) is the intended way to keep them consistent. year, when given, is the calendar year the epoch stands for; readers use it to propose a character’s age at that epoch (§9.1). An epoch token appears in filenames, so an application does not rename one; it adds a new epoch and the author forks files into it.

Readers SHOULD warn when a project file declares no epochs.

contributors lists the people on the project by owner handle. role: "director" designates who normally writes _official.json files. More than one director is allowed. Whoever creates a project is its first director: a writer that creates a project lists the creator with that role (§18.3); how a director appoints further directors is the project’s own business, edited in this file. Handles are names, not identities: a file can be created under any handle, and a commit’s actor and a filename’s author are different information a maintainer can compare. Readers SHOULD warn, and continue, when: a file is modified by someone other than its author; an _official.json is written by a non-director; a commit or sync introduces a file under a handle other than the current user’s. The repository maintainer decides.

The timeline tracks the project uses, in order, each with a kind and a display name. Shots are placed on av tracks; cues on audio tracks; a video track holds picture with no sound. The list above is the recommended default; a project may rename, reorder, add or remove. Placement (§11.3) refers to tracks by id; the editing application sees name. Track numbers are an export artefact (§15.1) and appear nowhere in a project.

format describes the delivery target. fps is an integer for integer rates, or { "num": 24000, "den": 1001 } for fractional rates — never a rounded decimal. resolution, aspect and color are strings; a project MAY add width, height, pixelAspect, colour primaries and transfer, audio sampleRate and channels.

The project lists its story roots in order with exactly one of seasons, episodes, sequences or scenes, whichever fits its structure. A short film can start with "scenes": ["1", "2"] and needs no season or episode scaffolding. Entries are tags or full references of the indicated type. An empty list is valid while starting from scratch. Order comes from these lists, not from sorting filenames.

A project file is an ordinary spec entity: copy it into your author handle, set forkedFrom, and edit its complete content. For example, pj_cartographer_suda_v1.json can name pj_cartographer_john123_v1 in forkedFrom, change lang to th-TH, and change its display name, credits and story roots. The tag remains cartographer so these are alternatives of the same project. No settings are inherited from forkedFrom.

Copying a project file neither copies nor translates all its descendants. Existing media references can be reused. Full references in the copied root list can keep existing story units selected while the remake is developed; switching those references or using author-first tags selects new story versions when desired. A project language is a default, not an instruction to translate existing script text. Users may copy other entities selectively, run a localisation plugin, or fork the Git repository.


A library entity is reusable across the story and has an epoch axis. Every one has the common header and may carry refs, preview, picks and a prompt object. The required content is small; Appendix A lists the recommended optional vocabulary. An entity with no references yet is valid.

refs holds the media a model is shown when generating this entity. It is either a plain array of image references, or an object of named groups:

{
"refs": {
"sheets": ["cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1"],
"turnarounds": ["references/kira/turnaround.png"],
"expressions": ["references/kira/expressions.png"]
},
"voice": {
"description": "low, unhurried, slight rasp",
"refs": ["references/kira/voice-calm.wav", "references/kira/voice-raised.wav"]
},
"preview": "references/kira/front.png"
}

Recommended group names — any may be used on any entity, and others may be added: images (generic), sheets, turnarounds, expressions, poses, details, materials, floorPlans, panoramas, lighting, motion, sound. Audio samples for a character’s voice live in voice.refs; an outfit has no voice. Every group is an array. Each epoch file has its own groups.

preview names the media shown for this entity in a library view. Absent, a reader may use the first sheet or image, then an image pick, then a placeholder. A preview is not automatically a model input.

picks selects generated takes by deliverable; a value is one media reference or an array when several frames are chosen. A reference used in several files reuses the bytes and nothing else.

{
"filmopen": 1, "type": "ch", "tag": "main-hero", "epoch": "30yo", "author": "john123", "v": 2,
"name": "Kira Voss",
"kind": "principal",
"aliases": ["KIRA", "the cartographer"],
"summary": "A cartographer who discovers her maps are rewriting the coastline.",
"appearance": {
"age": 30, "gender": "woman", "build": "lean, athletic", "skin": "olive",
"hair": { "color": "black", "length": "shoulder", "style": "loose, centre-parted" },
"eyes": { "color": "dark brown", "notes": "heavy lids" },
"marks": ["burn scar along the left forearm"],
"posture": "upright, still; moves only when necessary"
},
"personality": {
"summary": "Guarded, precise, allergic to being managed.",
"traits": ["methodical", "dry humour", "conflict-avoidant until cornered"],
"wants": "To prove the coastline is moving.",
"speech": "Short sentences. Rarely finishes a question."
},
"voice": { "description": "low, unhurried, slight rasp", "pitch": "low", "pace": "slow", "accent": "Lisbon", "lang": "pt-BR",
"refs": ["references/kira/voice-calm.wav"] },
"relationships": [{ "character": "spouse", "relation": "married to, separating" }],
"prompt": {
"positive": "30yo woman, olive skin, black shoulder-length hair, lean athletic build, burn scar left forearm, upright posture",
"negative": "cartoon, plastic skin, over-smoothed"
},
"creatorNotes": "Never smiles with teeth. Keep the scar visible in any shot showing the left arm.",
"picks": { "sheet": "cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1" },
"refs": { "sheets": ["cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1"] },
"preview": "cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1"
}
  • kindprincipal · supporting · extra · group · narrator · creature · animal. A group is a crowd described once.
  • birthYear — the character’s year of birth, an integer, the same in every epoch file of the character. When the project’s epoch has a year (§8.2), a reader proposes appearance.age = epoch.year − birthYear for a new epoch file, and the author may override it: a character born in 1950 is 50 at an epoch set in 2000 unless the file says 30.
  • aliases — every name a script or transcript might use. Essential for import and for Fountain export.
  • appearance, personality, voiceopen objects. Readers display whatever keys are present. Appendix A.1 recommends a vocabulary; none of it is required.
  • creatorNotes — for people; excluded from prompts unless a user explicitly includes it.
{
"filmopen": 1, "type": "of", "tag": "main-hero.suit", "epoch": "30yo", "author": "john123", "v": 1,
"name": "Charcoal business suit",
"garments": [
{ "item": "suit jacket", "color": "charcoal", "material": "wool", "fit": "tailored", "condition": "worn at the cuffs" },
{ "item": "shirt", "color": "white", "notes": "open collar, no tie" },
{ "item": "boots", "color": "brown", "material": "leather", "condition": "scuffed" }
],
"accessories": ["steel wristwatch, left wrist"],
"prompt": { "positive": "charcoal two-button suit, white open-collar shirt, worn brown leather boots, steel wristwatch" },
"picks": { "sheet": "cs_of_main-hero.suit_30yo_john123_v1_r7c21_john123_1" }
}
{
"filmopen": 1, "type": "lo", "tag": "cafe", "epoch": "1990s", "author": "john123", "v": 1,
"name": "Café Atlântico",
"aliases": ["CAFE", "the café"],
"kind": "interior",
"geography": { "city": "Lisbon", "country": "PT" },
"description": "Tiled floor, ceiling fans, a long zinc bar, harbour visible through steamed windows.",
"dressing": ["zinc bar", "six marble tables", "espresso machine, chrome", "framed harbour photographs"],
"lighting": { "default": "late afternoon, warm, low sun through the windows", "practicals": ["pendant lamps over the bar"] },
"sound": { "roomTone": "tiled, lively reverb", "ambience": "espresso machine, harbour gulls, distant traffic" },
"areas": ["cafe.bar", "cafe.kitchen"],
"prompt": { "positive": "1990s Lisbon café interior, tiled floor, zinc bar, ceiling fans, steamed windows, harbour beyond" },
"picks": { "sheet": "cs_lo_cafe_1990s_john123_v1_r7c21_john123_2" }
}

kindinterior · exterior · both. Sub-areas are locations with tag <parent>.<area> and their own epoch files.

{
"filmopen": 1, "type": "pr", "tag": "chart", "epoch": "default", "author": "john123", "v": 1,
"name": "The 1994 harbour chart",
"kind": "document",
"hero": true,
"description": "Hand-drawn nautical chart on yellowed linen-backed paper, folded twice, ink faded to sepia.",
"scale": "60 × 90 cm",
"condition": "creased, one corner water-stained",
"prompt": { "positive": "hand-drawn nautical chart, yellowed linen paper, sepia ink, fold creases, water stain" },
"picks": { "sheet": "cs_pr_chart_default_john123_v1_r7c21_john123_4" }
}

hero: true marks a prop the camera will see closely; it needs a stronger reference than background dressing.

{
"filmopen": 1, "type": "st", "tag": "war-grade", "epoch": "default", "author": "maria", "v": 1,
"name": "War — gritty, desaturated",
"description": "Episode 2 onward. Cold, grey, high contrast, grain.",
"genre": "drama",
"references": ["1970s political thrillers", "hand-processed 16mm"],
"look": { "stock": "16mm colour negative", "grain": "medium", "halation": "slight", "contrast": "high", "saturation": "low" },
"palette": ["#2b2f33", "#6b7076", "#b9bcb8", "#d9c9a3"],
"prompt": { "positive": "desaturated, cold grey palette, high contrast, 16mm grain, handheld",
"negative": "saturated, clean, glossy" },
"cdl": { "slope": [0.95, 0.98, 1.05], "offset": [-0.02, -0.02, 0.0], "power": [1.1, 1.1, 1.05], "saturation": 0.6 },
"lut": "style/war-grade/war-grade.cube",
"refs": { "images": ["cs_st_war-grade_default_maria_v1_r8af0_maria_1"] }
}

cdl records ASC CDL values; lut is a media reference to a .cube file. Both record colour intent and export with the timeline; whether an editing application applies them depends on its importer, and an exporter reports what it could not carry (§15.3).

{ "filmopen": 1, "type": "mi", "tag": "title-font", "epoch": "default", "author": "john123", "v": 1,
"name": "Title font", "kind": "font", "file": "misc/atlantico-bold.otf" }
{ "filmopen": 1, "type": "dc", "tag": "treatment", "epoch": "default", "author": "john123", "v": 2,
"name": "Treatment", "kind": "treatment",
"text": "Kira Voss draws maps for a living and discovers that her maps are redrawing the coast…",
"file": "docs/treatment-2026-08.docx" }

mi.kindfont · logo · title-card · audio · image · other. dc.kindtreatment · bible · pitch · beat-sheet · research · context · other. For a document, text is the editable FilmOpen content and survives a JSON-only checkout; file is an imported source or export. Either may be present alone.


Level Series Film / franchise File
1 season installment se_
2 episode act / arc ep_
3 sequence (optional) sequence (optional) sq_
4 scene scene sc_

Levels 1–3 are optional groupings. Each declares epoch, cast, locations and styles as defaults for its children, and an ordered list of those children. A scene narrows or overrides what it inherits from its unit; nothing is inherited between library files.

{
"filmopen": 1, "type": "ep", "tag": "e2", "author": "john123", "v": 1,
"name": "Episode 2 — Low Tide",
"synopsis": "Kira finds the first altered chart.",
"epoch": "mid-war",
"cast": [
{ "character": "main-hero", "epoch": "30yo", "outfit": "main-hero.suit" },
{ "character": "spouse", "epoch": "28yo" }
],
"locations": ["cafe", "archive"],
"styles": ["war-grade"],
"scenes": ["3", "4", "5", "archive-exit"],
"notes": "Cold open, no score until the reveal."
}

scenes (or sequences, or episodes on a season) fixes the order of children. If absent, a reader MAY propose natural (numeric-aware) tag order and MUST say so; a reproducible timeline export needs an explicit list. Cyclic or duplicate containment is reported.

A scene is one time and one place. Its file holds the blocks — the script — in order.

{
"filmopen": 1, "type": "sc", "tag": "5", "author": "john123", "v": 1,
"name": "The archive",
"location": "archive", "epoch": "mid-war",
"storyDay": 3, "time": "night", "weather": "rain against the skylight",
"cast": [{ "character": "main-hero", "epoch": "30yo", "outfit": "main-hero.suit" }],
"props": [{ "prop": "chart", "epoch": "default" }],
"style": "war-grade",
"synopsis": "Kira compares the two charts and realises the coastline moved.",
"purpose": "First proof. Turns the mystery from doubt to certainty.",
"targetDurationSec": 165,
"shots": ["5.1", "5.2"],
"cues": ["5.1", "5.2", "5.3"],
"blocks": [
{ "id": "bl_1", "kind": "action",
"text": "Kira spreads both charts across the reading table." },
{ "id": "bl_2", "kind": "dialogue", "character": "main-hero",
"text": "The bay was here last spring.",
"direction": "flat disbelief, does not look up" },
{ "id": "bl_3", "kind": "action",
"text": "Behind her, the window flashes white. A second later, the glass goes." },
{ "id": "bl_4", "kind": "dialogue", "character": "main-hero", "text": "Not now.", "offscreen": true },
{ "id": "bl_5", "kind": "transition", "text": "CUT TO:" },
{ "id": "bl_6", "kind": "note", "text": "Consider cutting bl_2 if pacing drags." }
]
}

storyDay is the script supervisor’s continuity day. purpose states what the scene does for the story, which is the single most useful field for an AI reading the project. shots and cues fix the order of the scene’s shots and cues; absent, natural tag order applies. locationEpoch overrides the scene epoch for the location alone.

Field Meaning
id bl_<n>; stable for the life of the block in this file. Never renumbered.
kind dialogue · action · narration · transition · title · lyric · note
text the line, the action, the title text
character short reference; dialogue, narration and lyric
direction performance note — the screenplay parenthetical
offscreen voiceover booleans — Fountain’s (O.S.) and (V.O.)
dual true when spoken simultaneously with the previous dialogue block
lang BCP 47 override for this block
startMs endMs observed timing, typically from import

Rules: new blocks take the next unused number; order is array order; moving a block to another scene copies it there with a new id; note blocks never render. Because ids are stable, two authors’ versions of a scene align by id with no diff algorithm (§13.3). Two forks that independently invent the same id are not the same line; a reader shows the text, and the Git base when available, rather than assuming.

Fountain FilmOpen
Scene Heading INT. CAFE – NIGHT scene location (+ kind), time
Action block action
Character + Dialogue block dialogue, character via aliases
Parenthetical direction
(O.S.) (V.O.) offscreen voiceover
Dual dialogue ^ dual
Transition block transition
Centered text block title
Lyrics ~ block lyric
Note [[ ]] block note
Section # story units
Synopsis = synopsis

The screenplay content round-trips. Title-page fields, emphasis, forced elements, scene numbers, page breaks and boneyards are not represented; an importer or exporter reports them, and a dc may keep the original text. Shot and sound information, which Fountain cannot express, lives in shots and cues.


A visual generation spec covering one or more blocks, with its placement on the timeline.

{
"filmopen": 1, "type": "sh", "tag": "5.2", "author": "john123", "v": 1,
"name": "Kira reacts; the window goes",
"scene": "sc_5_john123_v1",
"blocks": ["bl_3", "bl_4"],
"characters": ["main-hero"],
"size": "mcu", "angle": "low", "movement": "static",
"lens": { "focalMm": 50, "aperture": 2.0, "dof": "shallow" },
"lighting": { "key": { "direction": "side", "quality": "hard" }, "style": "low-key", "colorTempK": 3200,
"practicals": ["desk lamp"], "notes": "window flash as a single hard white hit from behind" },
"composition": { "headroom": "tight", "eyeline": "down-left", "screenDirection": "facing right" },
"durationSec": 5,
"continuity": ["chart still unfolded on the table", "scar visible, left forearm"],
"vfx": "glass shatter, practical-looking, no debris toward lens",
"deliverables": {
"firstFrame": { "model": "nanobanana", "prompt": "", "refs": ["cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1"] },
"clip": { "model": "seedance25", "prompt": "", "from": "firstFrame",
"inputs": ["vo_cu_5.3_john123_v1_r8af0_maria_1"] }
},
"place": { "track": "pic", "after": "sh_5.1", "offsetMs": 0, "inMs": 0, "outMs": 5000 },
"picks": { "firstFrame": "ff_sh_5.2_john123_v1_r8af0_maria_1",
"clip": "cl_sh_5.2_john123_v1_r8af0_maria_1" }
}

size, angle, movement, lens, lighting and composition use the vocabulary in Appendix A.4; all are optional. scene is a full reference to the scene version the block ids belong to (§6.4). In a deliverable, from names a preceding deliverable of the same shot and inputs names other media it depends on — a chosen dialogue take for lip sync, for instance; the batch records what was actually used. A still image placed as a shot takes its hold duration from durationSec.

An audio generation spec. A cue attaches to a scene, or to a sequence or episode when it spans scenes.

{
"filmopen": 1, "type": "cu", "tag": "5.1", "author": "john123", "v": 1,
"name": "Kira — the bay line",
"kind": "dialogue",
"scene": "sc_5_john123_v1",
"blocks": ["bl_2"],
"character": "main-hero",
"spoken": "The bay was here… last spring.",
"delivery": { "effort": "quiet", "emotion": "flat disbelief", "processing": "none" },
"model": "elevenlabs",
"place": { "track": "dx", "with": "sh_5.1", "offsetMs": 400, "gainDb": -3 },
"picks": { "voice": "vo_cu_5.1_john123_v1_r8af0_maria_1" }
}
{
"filmopen": 1, "type": "cu", "tag": "e2.1", "author": "maria", "v": 1,
"name": "Low Tide — main theme",
"kind": "music",
"unit": "ep_e2_john123_v1",
"music": { "tempoBpm": 72, "key": "D minor", "instrumentation": ["cello", "prepared piano", "tape hiss"],
"mood": "unease held under restraint", "structure": "sparse intro, swell at the reveal, cut on the flash" },
"sync": [{ "with": "sh_5.2", "offsetMs": 0, "event": "cut on the flash" }],
"anchors": [{ "scene": "sc_5_john123_v1", "blocks": ["bl_3"] }],
"model": "suno",
"place": { "track": "mx", "with": "scene:3", "offsetMs": 0, "fadeInMs": 2000, "fadeOutMs": 4000, "gainDb": -12 },
"picks": { "music": "mu_cu_e2.1_maria_v1_r91c0_maria_2" }
}
  • kinddialogue · narration · music · sfx · ambience.
  • Text comes from the referenced blocks, concatenated in order. spoken, if present, replaces it — for pauses, pronunciation, numbers spelled out, a translated line. Readers surface “spoken differs from script” as an intentional state, and warn when the block has changed since.
  • ssml (W3C SSML) is an alternative to spoken for models that accept it; use one or the other.
  • A cue attached to a unit that needs specific blocks from specific scenes uses anchors, since block ids are local to a scene.

place states where a shot or cue sits on the timeline. Every field is optional; a scene with no placement at all is a straight sequential cut on the first av track, in the scene’s shots order.

Field Meaning Default
track track id from the project (§8.4) first av track for shots; dx for dialogue, vo narration, sfx, amb, mx music
after anchor: start at this item’s end plus offsetMs. A shot tag, or prev prev for shots
with anchor: start at this item’s start plus offsetMs. A shot tag, scene, or scene:<tag> for cues, the first shot covering the cue’s first block
offsetMs signed milliseconds from the anchor 0
inMs outMs trim within the source take, from its first frame; outMs exclusive whole take
audio false to place picture only from an av clip true
gainDb pan mix 0, 0
fadeInMs fadeOutMs audio fades 0
transitionIn { "type": "cut" | "dissolve" | "fade", "ms": n } cut

Rules:

  • after and with are the only anchors, and a placement uses one of them. prev is the previous shot in the scene’s order, not the previous file on disk. Anchor cycles, missing anchors and negative starts are reported.
  • Two items on one track cannot overlap. A negative offsetMs after a picture cut on the same track is a collision and is reported. Overlaps go on another track: a second picture track for an overlay, or — the common case — the dialogue track for a J-cut, where the next line begins before the picture cuts: "with": "sh_5.2", "offsetMs": -1500 on the cue.
  • A dissolve or fade needs source handles: media beyond the nominal trims on both sides. The exporter checks the picked takes and reports when handles are short; it never shortens the cut.
  • Changing a duration ripples downstream through the after chain, as a rough assembly should.
  • Timings are numbers in milliseconds. An exporter converts to the project frame rate and reports any rounding it applies. Equal boundaries must round identically; ties round toward the later frame. Source trims must also respect the source media rate and available range.

An observation at 12:04.625 in an imported film belongs in the batch’s source timing (at). A placement anchored to scene uses an offset from that scene’s start, not the source film’s start. The first shot defaults to the scene start when prev has no predecessor. Scenes begin sequentially in the selected story order; a scene’s length is the latest end of its placed shots and scene cues. Unit-level cues do not lengthen individual scenes. Cross-scene anchor cycles are reported.


12.1 Takes are discovered, never recorded in the entity

Section titled “12.1 Takes are discovered, never recorded in the entity”

A take is a media file whose name embeds its source stem (§5.4), or an imported file named in a batch. No entity file lists its takes. A reader finds them by scanning the media root (§4.4) — and the project folder itself, so that a project without a manifest keeps working — and by reading batches; the batch says how they were made; the only thing an entity’s author writes about takes is picks. This is what lets one person render another’s shot without touching their file, and lets a character page show every sheet anyone has rendered for it.

{
"filmopen": 1, "type": "rd", "id": "8af0", "author": "maria",
"kind": "render", "tier": "final",
"created": "2026-09-06T15:30:00Z", "status": "done",
"items": [
{ "n": 1, "source": "sh_5.2_john123_v1", "deliverable": "clip",
"model": "seedance25", "platform": "fal",
"prompt": "…the fully resolved prompt as sent…",
"params": { "seed": 41221, "steps": 30, "durationSec": 5, "fps": 24 },
"inputs": ["ff_sh_5.2_john123_v1_r8af0_maria_1", "vo_cu_5.3_john123_v1_r8af0_maria_1"],
"output": "s1/e2/sc5/cl_sh_5.2_john123_v1_r8af0_maria_1.mp4",
"sha256": "9d02…", "durationMs": 5000, "elapsedMs": 61400, "costUsd": 0.42,
"thumbnail": "s1/e2/sc5/cl_sh_5.2_john123_v1_r8af0_maria_1.thumb.jpg",
"human": { "selectedFrom": 4, "promptEditedBy": "maria" } }
]
}
Field Meaning
kind render · import · plugin · manual
tier preview · final — cheap fast previews versus locked finals
output media reference — usually a project-relative path
take take number in a generated filename when it is not 1
sha256 recorded on ingest. Names carry meaning; hashes carry identity; together they survive renames, moves and duplicates
durationMs / elapsedMs length of the output media / time the job took
costUsd absent when unknown, never zero
human the human contribution: candidates reviewed, who edited the prompt, manual edits. The authorship evidence (§16.5)
status jobId attempts error job notes for a pending, failed or partial batch; a batch may be updated in place
thumbnail poster proxy waveform display derivatives; never model inputs by themselves
mimeType bytes width height fps frameCount sampleRate channels measured media properties; measured on ingest if absent, never invented

12.3 Import batches — describing a film that exists

Section titled “12.3 Import batches — describing a film that exists”

kind: "import" records material taken from an existing work. Its takes are named like any other; the renderer is the importing author, so the take always finds its batch.

{
"filmopen": 1, "type": "rd", "id": "0001", "author": "john123",
"kind": "import",
"source": { "file": "the-cartographer-2019.mp4", "sha256": "e1a4…", "fps": 24, "durationMs": 5412000 },
"created": "2026-09-06T09:00:00Z", "status": "done",
"items": [
{ "n": 1, "source": "sh_5.2_john123_v1", "deliverable": "firstFrame",
"at": { "ms": 724625 }, "output": "s1/e2/sc5/ff_sh_5.2_john123_v1_r0001_john123_1.png", "sha256": "77b0…" },
{ "n": 2, "source": "cu_5.1_john123_v1", "deliverable": "voice",
"at": { "startMs": 724625, "endMs": 727200 }, "output": "s1/e2/sc5/vo_cu_5.1_john123_v1_r0001_john123_1.wav", "sha256": "31cd…" },
{ "n": 3, "source": "ch_main-hero_30yo_john123_v1", "deliverable": "baseImage",
"output": "references/kira/front.png", "width": 2048, "height": 2048 }
]
}

Reverse-engineering a film produces ordinary FilmOpen files: blocks from a transcript with timing, shots from detected cuts with place, characters with aliases from speaker labels and appearance from observed frames, takes extracted at timecodes. Each such entity carries "source": { "batch": … }. An import item for a file that keeps its own name, as in item 3, simply names it.

kind: "plugin" records a plugin run: the plugin stem, the settings used, and the files produced under the run’s derived handle (§14.3).

A batch may be emitted instead of submitted: render/rd_<id>_<author>/ holds one workflow file per item — for ComfyUI, a workflow JSON, labelled as UI graph or API payload — each already named for the output it should produce. The workflows run anywhere; the outputs are dropped into the project; a reader matches them by name and records hashes.

On every scan a reader SHOULD: parse every filename; attach takes to sources; record sha256 and measured properties for new outputs in their batch; flag outputs whose hash no longer matches their record; and report unknown files without moving them. On a network timeout a reader checks an existing provider job before submitting another paid request, and keeps completed items when one fails. A reader MUST NOT assume it is the only writer.


13. Versions, official, compare and commentary

Section titled “13. Versions, official, compare and commentary”

A version is a whole file. All are readable; the highest is the author’s working copy. Files are edited in place and nothing is frozen; a filmmaker may edit sc_5_john123_v1.json for months. Fork on your own version creates v(n+1) as a copy; fork on someone else’s creates your v1 (or next) with forkedFrom set. Prior states of a file are Git’s business.

{ "filmopen": 1, "type": "ch", "tag": "main-hero", "epoch": "30yo",
"official": "ch_main-hero_30yo_maria_v1",
"setBy": "john123", "setAt": "2026-09-06T14:20:00Z", "note": "Maria's. Using hers." }

Written by a director. Applies to every versioned type. Everything downstream reads through official unless a viewer is looking at their own version (§6.1). Editing a version that official names changes what the whole project sees; readers SHOULD say so and offer to fork instead, and then allow it. A project with no chosen version for an entity simply has no pointer file: to withdraw a choice, a director deletes the pointer; an application that offers official as a toggle removes the file when the toggle is cleared and writes it, with setBy and setAt, when it is set.

The core collaborative act, and the only merge operation. Versions of one entity are shown as columns:

  1. the viewer’s own working version, if any;
  2. the version the viewer has marked prefer in their commentary, if it differs from official;
  3. official;
  4. any others the viewer adds — including other epochs of the same entity, plugin output, and a sync client’s conflicted copy.

Each column offers copy-left for the whole entity and beside each attribute. Copying a nested object copies the subtree. A viewer with no version of their own who copies anything gets a v1 created — copy-left is fork when needed. Copying is not confirmed; the re-rendered result is the check.

Scenes compare by block id.

Case Shown as Copy-left does
same id, same text collapsed: 6 unchanged
same id, different text side by side, changed words marked replace mine
id only in theirs a gap in my column at their position insert it
id only in mine greyed: not in theirs delete mine

“Not in theirs” does not prove they deleted it; when a Git base is available a reader can tell an addition from a deletion.

One file per author per entity, covering all its epochs, versions and takes.

{
"filmopen": 1, "type": "cm", "target": "ch_main-hero", "author": "maria",
"prefer": "ch_main-hero_30yo_susan_v1",
"entries": [
{ "on": "ch_main-hero_30yo_john123_v2", "at": "2026-09-06T10:02:00Z", "rating": 1,
"text": "I really like the tattoo." },
{ "on": "cs_ch_main-hero_30yo_john123_v2_r8af0_maria_1", "at": "2026-09-06T11:15:00Z", "rating": 1 },
{ "on": "ch_main-hero_30yo_susan_v1", "at": "2026-09-06T12:40:00Z", "rating": -1,
"text": "Too young for the mid-war scenes.", "block": "bl_2" }
]
}

on is a version stem or a media reference. rating is 1, -1, or absent. An entry may name a block, a field or a gitCommit for context. prefer is the author’s pick — the version they would choose; it orders compare columns and an application shows it (a check) beside official (a star). Changing a pick rewrites only the author’s own commentary file; a viewer without one gets an empty one with prefer set. Readers consolidate every cm_ file for an entity into one feed. Review can equally happen in pull requests or conversation; commentary is not required.


Hooks and plugins are JavaScript, ECMAScript 2020, single file, no import/require. Functions may be async. Code receives one argument, ctx, and returns a value; it never mutates project state. Hooks and plugins are data until an application chooses to run them; plain readers never execute code.

Code runs with JSON, Math, String, Number, Array, Object, RegExp, Map, Set, Promise and nothing else: no filesystem, no network, no clock, no randomness, no timers. That list is the API surface, not the security boundary: an application MUST isolate untrusted code — runtime, memory and output limits, no ambient filesystem or shell, no network except the services in §14.4 — and must protect its credentials, which never enter project JSON or plugin output. The application decides which code the user trusts to run and re-evaluates that trust when the code changes. An official pointer alone does not grant execution permission. Trust decisions belong to the application, not to the film files.

Short functions inside mo, pl, st and pj files, keyed by name.

Hook Runs Receives Returns
validate before save or render { entity, project, params? }; resolved generation parameters when rendering true or an error string
buildPrompt assembling a prompt { shot | cue | entity, cast, location, style, project } string, or { positive, negative, refs, params }
buildRequest submitting to a platform { model, platform, prompt, inputs, params } { url, body, headers? }
parseResponse platform replies raw response { output, durationMs?, elapsedMs?, cost? }
estimateCost before a batch { model, platform, params } number, USD, or null for unknown
postProcess after ingest { item, take } modified item

Prompt precedence: an explicit prompt on the deliverable; else the model’s buildPrompt; else a simple documented default. Builders include selected descriptions and references, never every note in a file. Parameters a provider does not support are reported, not silently dropped.

{
"filmopen": 1, "type": "mo", "tag": "seedance25", "author": "paul", "v": 3,
"name": "Seedance 2.5", "vendor": "ByteDance", "kind": "video",
"capabilities": ["text2video", "image2video"],
"inputs": { "prompt": "string", "image": "file?", "durationSec": "int 3..15", "seed": "int?" },
"defaults": { "durationSec": 5, "fps": 24 },
"platforms": ["fal", "runninghub"],
"hooks": {
"buildPrompt": "(ctx) => [ctx.shot.prompt, ctx.style && ctx.style.prompt.positive, ctx.cast.map(c => c.prompt.positive).join(', ')].filter(Boolean).join('. ')",
"validate": "(ctx) => !ctx.params || ctx.params.durationSec == null || ctx.params.durationSec <= 15 || 'Seedance 2.5 clips are 15 s max'"
}
}
{
"filmopen": 1, "type": "pl", "tag": "fal", "author": "paul", "v": 1,
"name": "fal.ai", "kind": "remote", "baseUrl": "https://fal.run",
"auth": { "kind": "apiKey", "keychain": "filmopen/fal" },
"models": { "seedance25": "fal-ai/bytedance/seedance/v2.5" },
"hooks": {
"buildRequest": "(ctx) => ({ url: ctx.platform.baseUrl + '/' + ctx.platform.models[ctx.model.tag], body: { prompt: ctx.prompt, image_url: ctx.inputs.image, duration: ctx.params.durationSec, seed: ctx.params.seed } })",
"parseResponse": "(res) => ({ output: res.video.url })",
"estimateCost": "(ctx) => 0.07 * ctx.params.durationSec"
}
}

mo.kindimage · video · audio · music · tts · analysis. Analysis capabilities include shot-detect, transcribe, diarize, face-cluster, describe-frame, describe-video. Response parsers set durationMs only from the output media duration. Inference or request runtime belongs in elapsedMs, converted from the provider’s documented units; otherwise omit it. Secrets never appear in a project; auth.keychain names a key the application holds.

A plugin is a manifest and a code file named after it, in plugin/<tag>/.

{
"filmopen": 1, "type": "pg", "tag": "locale", "author": "paul", "v": 2,
"name": "Localiser", "api": 1,
"description": "Translates dialogue and relocates settings to a target culture.",
"entry": "pg_locale_paul_v2.js",
"applies": ["sc", "cu", "lo"],
"hooks": ["transform"],
"capabilities": { "ai": true },
"settings": { "target": { "type": "string", "default": "th-TH" }, "relocate": { "type": "boolean", "default": true } }
}
Hook Purpose Receives Returns
transform rewrite entities — polish, translate, restyle, relocate { entities, project, settings, ai? } the modified entities
analyze comment without changing — continuity, pacing, consistency same commentary entries
import turn a foreign file into FilmOpen entities { file, project, settings, ai? } entities and an optional batch
export turn entities into a foreign format { entities, project, settings } { filename, content }

Rules:

  • api names the host interface version; a host reports versions it does not support.
  • A plugin has read access to the whole project and operates on the entities the user selected.
  • A plugin never writes files. It returns entities; the application writes them as new versions under the running user’s derived handlejohn123.locale-th — all starting at v1, and records a kind: "plugin" batch. A second run is v2. If the source files changed while the run was in progress, the application shows that rather than overwriting.
  • Plugin output is never merged into the owner’s own versions. Accepting it is pointing official at it, entity by entity or as a whole run; taking parts is copy-left; hand-fixing it is forking it.
  • capabilities.ai grants await ctx.ai.complete(prompt, options){ text, usage?, cost? } and, where the host supports vision, await ctx.ai.describe(media, prompt, options), routed through the user’s configured platform and key. This is the only path from the sandbox to a network. The application owns paid-call consent, cancellation and retries within the user’s configured limits.

A localisation plugin is the canonical example: fork the repository for a new territory, run the plugin, point every _official.json at the run. The original is untouched in the same tree. An import plugin with ai: true is the reverse-engineering tool of §12.3.

Models, platforms, plugins and optionally styles live in a public repository the format maintainer controls, using exactly the conventions above: versions, authors, _official.json, commentary. Share on a forked entry is a pull request; the maintainer merges and, once tested, moves _official.json. No central service is needed to use a locally installed entry.

The library is installed beside projects, not inside them: one copy per machine (or per user), in a folder the application manages and can update on its own schedule — a git pull, a download — without touching any project and without a new build of the application. A project therefore need not contain mo_, pl_ or pg_ files at all; a reader resolves references to models, platforms and plugins against the project first and then against the library. A project MAY hold its own — a fork of a library entry made inside the project as one would fork a character, or a private model — and those take part in resolution exactly like any other entity of the project. The same full stem present in both the project and the library is reported as a duplicate; the project’s file is used. Where the library folder lives is an application preference (§4.2: application caches and indexes live wherever the application chooses); a project’s manifest may later name the library it was seeded from (§4.4, library, reserved).


OpenTimelineIO and every editing application have separate video and audio tracks; there is no audio-visual track. A FilmOpen av track exports as a video track and an audio track that share a name. The mapping is deterministic:

Video tracks: each av and video track in declared order → V1, V2, … Audio tracks: the sound of each av track in declared order → A1, A2, …; then each audio track in declared order, continuing the numbering after those av audio tracks Every exported track carries its FilmOpen name.

Nobody in a project ever writes a track number. A cue on dx lands on a track labelled Dialogue whether that is A3 or A4 in a given project.

One flat timeline per episode or film. Tracks span the whole timeline; scenes appear in unit order (§10.1) with a marker at each scene start carrying the scene stem, and a marker on each clip carrying its shot or cue stem. No nested compositions — importers handle them badly.

Timings are computed from place by walking after chains, in milliseconds, then converted once to the project rate using the exact rate ratio for fractional rates given as num/den. Millisecond inputs may require rounding; conversion does not promise sample-exact interchange. Source in/out are start-inclusive, end-exclusive. Source timecode and timeline position are distinct.

Before writing, the exporter checks and reports: unresolved or ambiguous references; missing or unreadable media; trims beyond the take’s measured duration; anchor cycles; same-track collisions; transitions without sufficient handles; a cue’s embedded audio duplicated on two tracks; any timing rounding applied. An incomplete project is still editable and still exports as a labelled preview if asked; a final conform stops on the first impossibility and names the field.

Every export ships a manifest — source stem, media path, hash, target path, source and timeline ranges — and the report above.

OTIO represents tracks and names, clips, source ranges, gaps, markers, transitions and linear speed. Support in each editing application or adapter must be tested. Metadata can preserve other intent, but does not guarantee that the editor applies grades, effects, audio automation or titles. Consequently:

  • Export is a conform of raw footage: picked takes placed as specified, nothing more. The editor finishes in the editing application. CDL and LUT values ship as sidecars.
  • Additional export formats are optional. Final Cut Pro 7 XML (xmeml, .xml) and Final Cut Pro X XML (.fcpxml) are different formats. Premiere supports the former; direct FCPXML import requires conversion. Choose a tested target profile rather than promising generic round-trip fidelity. CMX3600 EDL is a limited fallback, with unsupported structure reported. Adobe interchange documentation
  • An editor’s export back to FilmOpen is lossy by nature. The format does not attempt to hold what OTIO cannot carry.

15.4 Never overwrite the editor’s timeline

Section titled “15.4 Never overwrite the editor’s timeline”

Two things change after the editor has started: takes get re-rendered, and the editor changes timing. They are handled by different mechanisms, and neither one emits a new timeline over the editor’s.

Re-rendered takes → relink. Export media into conform/ under stable slot names that do not change when a pick changes — sh_5.2_clip.mp4, cu_5.1_voice.wav. Picking a new take and re-exporting overwrites the slot file as a deliberate act. The editor’s timeline references the same path; the editor can refresh or relink the media while retaining timeline edits. Preservation of grades, effects and transitions depends on the editor and compatible replacement media; verify it in the named target version rather than guaranteeing it. A new take of a different duration no longer fits the editor’s in/out — exactly as when a live-action take is swapped — and the report MUST say so.

Editor’s timing → import. When the editor exports OTIO, a reader MAY read it and propose changes to place — track by name, anchor as with: scene, offset, in/out. Clip identity comes from the media filename or slot name and from the markers. Split clips, repeated occurrences and structure the format cannot express are reported, not compressed into one wrong placement. FilmOpen’s representation stays in step with the editor’s decisions without ever emitting over them.

New material → additive. New scenes and shots export as a separate timeline in the same project, or as a bin of slot media. The editor drags them in.

Under this model the editor can begin finishing as soon as an assembly exists. What still has to wait is structural change — reordering shots after cutting has begun — which no format can make free.


Domain Standard Status
Screenplay Fountain adopted, import and export of screenplay content (§10.4)
Screenplay FDX import via Fountain, with a loss report
Subtitles and timing SRT, WebVTT adopted; import produces timed blocks, export from dialogue cues
Production entities MovieLabs OMC v2.8 / OMC-JSON concepts aligned; export mapping (Appendix C)
Colour ASC CDL, .cube adopted in styles; exported as sidecars
Edit OpenTimelineIO; optional EDL CMX3600, FCP7 XML, FCPXML export as conform; OTIO import proposes timing
Speech SSML optional on cues
Provenance C2PA, XMP embedded on export (§16.5)
Workflows ComfyUI workflow JSON native local workflow and offline batches
AI character definition Character Card V2 precedent; creatorNotes and PNG-embedded JSON borrowed
Location, prop, shot, cue, render batch none exists; defined here

The Ontology for Media Creation, published by MovieLabs and governed with the major studios, defines Character, Location, Prop, Costume, Scene, Shot, Sequence, Asset and Depiction as first-class entities. FilmOpen’s entity list is nearly OMC’s; picks is OMC’s Depiction — the link from a narrative object to the asset that depicts it. FilmOpen keeps its own readable field names and maps on export (Appendix C). FilmOpen is not an OMC implementation. The OMC-JSON schema repository and the ontology documentation carry their own licences; confirm them before reusing artefacts.

The AI-conversation community’s character format embeds its JSON inside the PNG that pictures the character. FilmOpen readers MAY embed the entity JSON in a concept-sheet PNG’s tEXt chunk under the key filmopen, so a shared image carries its own definition. The JSON file remains authoritative; embedding changes the image’s bytes and hash.

ComfyUI embeds the workflow that produced an image in the image’s metadata. FilmOpen readers SHOULD do the same for takes produced through ComfyUI, and SHOULD preserve any embedded workflow found on ingest.

Each batch item is the provenance of its take. On export a reader SHOULD write it into the output’s XMP and, where tooling allows, emit a C2PA manifest: trainedAlgorithmicMedia for rendered takes, and for imported takes whatever the batch’s source states — imported media may itself be generated or composite and is never labelled camera capture by default. The human block records the human contribution to each output; it is evidence, not a legal determination. A reader MAY produce an authorship report walking batches, picks and commentary.


The format is meant to be edited without any FilmOpen application. For a person or an agent:

  1. Never rename an entity file. Create a new version.
  2. Edit only files whose author is you. To change someone else’s, fork it: copy, rename to your handle and v1, set forkedFrom. A delegated edit to another’s file is still valid data; the maintainer decides whether to accept it.
  3. Prefer not to edit a version named by _official.json. Fork instead. If you must, you are changing what everyone sees.
  4. Keep the header in step with the filename.
  5. Never renumber block ids. Add with the next unused number; reorder by moving array elements.
  6. Short references in story files; full references in forkedFrom, prefer, on; media references in picks, refs, preview, file.
  7. Do not write takes into entities. Drop conforming media beside the JSON, or name it in a batch; a reader will find it. If you cannot name it, use _inbox/.
  8. No secrets in the project.
  9. Preserve fields you do not understand.
  10. What the format cannot say goes in notes, or in x for structured application data.

A machine generating a project from a film should produce: pj_; one ch_ per identified speaker with aliases; lo_ per setting; sc_ files with timed blocks; sh_ per detected cut with place; cu_ per dialogue line; an import batch; extracted takes — and nothing else.


A conforming file is UTF-8 JSON with no duplicate keys, named by the grammar for its category, with the required header for that category. Writers SHOULD pretty-print with two-space indentation for readable diffs; whitespace is not semantic.

A conforming reader:

  • MUST parse every filename by the grammars in §5.4 and dispatch on type;
  • MUST read filmopen-project.json when present (§4.4) and use the media root it names; MUST NOT refuse a folder that lacks it;
  • MUST resolve references by §6;
  • MUST discover takes by scanning and by batches, and MUST NOT require entities to list them;
  • MUST preserve unknown fields and the x object when rewriting a file it authors;
  • MUST NOT assume it is the only writer, and MUST NOT move, rename or delete files it does not understand;
  • SHOULD warn, and continue, on: a header that disagrees with its filename; a file whose author is not the current user being modified; an _official.json written by a non-director; a version named by _official.json being edited; a segment longer than 10 characters; a reference that resolves ambiguously; an explicit epoch that does not exist; a project file that declares no epochs.

Authorship warnings are not validity tests. A file edited by someone other than its named author is still a conforming file; whether it is accepted is the maintainer’s decision.

A conforming writer:

  • MUST write lowercase filenames matching §5;
  • MUST write filmopen-project.json when creating a project folder (§4.4), and SHOULD write the creator’s first project version with the creator as director and at least one epoch (§8.2, §8.3);
  • MUST write the required header for the file’s category;
  • SHOULD write only under the current user’s owner handle or a workspace of it;
  • MUST NOT delete another author’s files.

JSON Schemas for every file type are intended to be published at https://filmopen.ai/schema/1/<type>.json, CC0, and usable offline. Until released and tested, the URLs are publication targets. Schemas mark the header and category-specific required fields (including one project story-root list) as required, and enumerate the vocabularies in Appendix A without restricting free-form objects to them. Cross-file resolution and timeline arithmetic are reader and exporter work beyond schema validation.

A first implementation should demonstrate at least: a blank project written, rendered and exported with no account and no Git; the same project opened in a text editor and a third-party reader; a delegated edit shown clearly to a maintainer; base and two epochs of one character used in different scenes with independent references; another contributor’s shot rendered and discovered without changing their file; an imported file with a non-conforming name referenced through a batch; a plugin run compared and partly accepted; an interrupted provider job recovered without a second paid request; and an integer-rate and a fractional-rate export opened in a named editing application.


  • Overlays or inheritance. Every file is complete.
  • Reserved key prefixes, canonical serialisation, hash-addressed JSON, UUIDs. Any editor works.
  • Per-field attribution, immutable revisions, history files. Credit lives in forkedFrom, by, batches and commentary; history lives in Git.
  • Frame- or sample-unit timing fields. Milliseconds only; exporters convert and report rounding.
  • Cast instances. A scene selects one appearance per character; a story needing two at once uses two tags.
  • A full edit model — L-cut audio splits beyond cue placement, parameterised transitions, effects. That is the editing application’s job; the format hands it a conform with timing.
  • Alternate cuts as first-class objects. An alternate assembly is a set of shot versions with different place. An explicit assembly entity may be added in a later version.
  • Identity or authentication. Handles are names. Trust is the repository gatekeeper’s.

Everything in this appendix is optional. It exists so that writers — human, scripted or AI — share a vocabulary, so that comparisons line up attribute by attribute, and so that a reverse-engineering tool has a checklist of what to look for. Free-form objects (appearance, personality, lighting, …) may carry any additional keys.

Enumerated values are lowercase strings. Where a field accepts free text, the enumeration is a suggested vocabulary, not a constraint. “media ref” means a media reference as defined in §6.3.

Key Type Values / notes
kind enum principal supporting extra group narrator creature animal
aliases string[] script cue names, nicknames, transcript labels
summary string one or two sentences
arc string how the character changes across the story
birthYear integer year of birth, the same in every epoch file; with the epoch’s year a reader proposes appearance.age (§9.1)
appearance.age number or string 30, "late thirties"; proposed from birthYear and the epoch’s year, overridable
appearance.gender string free
appearance.ethnicity string free; optional; for continuity only
appearance.species string human default
appearance.heightCm appearance.weightKg number
appearance.build string slight lean athletic average stocky heavy
appearance.skin string tone, texture, notable features
appearance.hair.color .length .style .texture string length: bald buzz short chin shoulder long; texture: straight wavy curly coily
appearance.facialHair string
appearance.eyes.color .shape .notes string
appearance.face string shape, notable features
appearance.marks string[] scars, tattoos, birthmarks, with location
appearance.glasses appearance.jewellery string worn habitually
appearance.posture appearance.gait string how they stand and move
appearance.hands appearance.teeth string often visible in close-ups
appearance.notes string
personality.summary string
personality.traits string[]
personality.wants .needs .fears string
personality.speech string rhythm, vocabulary, verbal habits
personality.mannerisms string[] physical habits
voice.description string
voice.pitch enum very-low low mid high very-high
voice.pace enum slow measured normal quick rapid
voice.timbre string breathy rasp nasal resonant thin
voice.accent voice.lang string free; BCP 47
voice.refs media ref[] reference voice samples for this character and epoch
voice.providerBindings object platform tag → provider voice id; samples and text remain the portable truth
relationships[] object { character, relation }
defaultOutfit tag outfit used when a scene does not say; resolved at the character’s epoch
prompt.positive prompt.negative string
creatorNotes string for people; excluded from prompts unless explicitly included
picks.sheet picks.turnaround picks.expressions media ref or media ref[] chosen concept sheets
refs media ref[] or grouped object §9.0
preview media ref shown in library views
Key Type Values / notes
garments[] object { item, color, material, fit, pattern, condition, notes }
accessories string[]
footwear string
palette string[] hex or names
era string
condition enum new worn distressed damaged bloodied wet muddy
prompt creatorNotes picks.sheet refs preview as for character; no voice

Location

Key Type Values / notes
kind enum interior exterior both
aliases string[] slugline names
geography object { city, region, country, coordinates }
era string period of the architecture and dressing
architecture string style, materials, scale
description string
dressing string[] props and set dressing habitually present
areas tag[] sub-locations <parent>.<area>
lighting.default string
lighting.practicals string[] visible light sources
lighting.windows string direction, size, coverings
sound.roomTone sound.ambience string
weather string default, exteriors
timeOfDay enum default; see A.5
access string entrances, exits, sightlines
palette string[]
prompt creatorNotes picks.sheet refs preview groups often used: floorPlans, panoramas, lighting

Prop

Key Type Values / notes
kind enum vehicle weapon document device furniture food tool clothing-item creature other
hero boolean seen close; needs a strong reference
description scale material condition string
interaction string how characters handle it
sound string what it sounds like
prompt creatorNotes picks.sheet refs preview
Key Type Values
size enum ecu extreme close-up · cu close-up · mcu medium close-up · ms medium · mls medium long / cowboy · ls long / wide · els extreme long · ots over-the-shoulder · pov · two-shot · group · insert · establishing · master
angle enum eye low high dutch overhead birds-eye worms-eye profile three-quarter frontal reverse
movement enum static pan tilt push-in pull-out truck pedestal crane boom handheld steadicam tracking arc orbit zoom-in zoom-out rack-focus whip-pan drone
movementNotes string speed, start and end framing
lens.focalMm number
lens.aperture number f-stop
lens.dof enum shallow moderate deep
lens.anamorphic boolean
lens.focus string what is sharp; rack targets
speed enum normal slow fast timelapse freeze
speedFactor number 0.5 = half speed
lighting.key.direction enum front side three-quarter back rim top under
lighting.key.quality enum hard soft
lighting.style enum high-key low-key natural practical silhouette neon firelight mixed
lighting.colorTempK number
lighting.contrast enum flat normal high extreme
lighting.practicals string[]
lighting.notes string
composition.headroom enum tight normal loose none
composition.leadRoom string
composition.eyeline string
composition.screenDirection string facing left moving right
composition.symmetry enum centred thirds asymmetric
composition.depth object { foreground, midground, background }
composition.blocking string where characters are and move
characters tag[] visible in frame; appearance and outfit come from the scene cast
props tag[] visible in frame
action string what happens within the shot, in order
continuity string[] states that must match adjacent shots
vfx string
aspect string override of project aspect
durationSec number target; the hold for a still
deliverables object keys storyboard firstFrame lastFrame keyframes clip; each { model, platform?, prompt, negative?, refs?, from?, inputs?, params? }
place object §11.3
picks object one media ref, or several frames, per deliverable
coverage string notes on what else was shot for this beat
Key Type Values
location tag locationEpoch overrides the scene epoch for the location
epoch epoch
storyDay number continuity day
time enum dawn morning midday afternoon golden-hour dusk blue-hour night late-night
weather string clear overcast rain storm fog snow wind heat-haze … free
cast[] object { character, epoch?, outfit?, notes? } — one selection per character
props array tags, or { prop, epoch } for an explicit state
style tag
synopsis string
purpose string what the scene does for the story
mood string
pacing enum slow measured brisk frantic
targetDurationSec number
sound object { ambience, notes }
continuity string[]
vfx string
shots cues tag[] order of the scene’s shots and cues
blocks array §10.3

Units add scenes / sequences / episodes for order, and cascade epoch, cast, locations, styles.

Key Type Values
kind enum dialogue narration music sfx ambience
scene or unit full ref what the blocks belong to
blocks id[] span, within scene
anchors[] object { scene, blocks } when a unit-level cue cites specific scenes’ blocks
character tag dialogue and narration
spoken string replaces block text
ssml string alternative to spoken
lang string BCP 47
delivery.effort enum whisper quiet normal raised shout
delivery.emotion string
delivery.processing enum none phone radio pa distant underwater
delivery.adr boolean replaces on-camera sound
music.tempoBpm music.key
music.instrumentation string[]
music.mood music.genre music.structure string
music.reference string a description, never a copyrighted recording as a target
music.lyrics string
music.stems object named stem → media ref, when a delivered piece is split
sfx.description string
sync[] object { with, offsetMs, event } — hit points
model platform tag
place object §11.3 incl. gainDb pan fadeInMs fadeOutMs
picks object voice music sfx ambience
Key Type Values
genre string
references string[] descriptive — eras, movements, techniques
look.stock string film stock or digital emulation
look.grain enum none fine medium heavy
look.halation enum none slight strong
look.contrast enum flat normal high extreme
look.saturation enum mono low normal high
look.sharpness enum soft normal crisp
look.vignette enum none slight strong
palette string[] hex
colorTempBias enum cool neutral warm
aspect letterbox string / boolean
lensCharacter string flare, breathing, distortion
cdl object { slope[3], offset[3], power[3], saturation }
lut media ref .cube
prompt refs preview groups often used: images, lighting, sound
Kind Fields
dialogue character text direction offscreen voiceover dual lang
action text characters?
narration character text lang
transition textCUT TO: DISSOLVE TO: FADE OUT.
title text font? (misc tag) position? durationMs?
lyric character text
note text

All kinds: id; optional startMs, endMs.

author v forkedFrom? label? name kind (short feature miniseries series) genre[] year logline synopsis lang rating format{aspect,fps,resolution,color,…} runtimeMin credits[{role,name,handle}] epochs{<token>: {label?, order?, year?}} (at least one; the first is the default) contributors tracks license hooks, and exactly one story root list: seasons, episodes, sequences or scenes.

Recommended genre tokens: action adventure animation biography comedy crime documentary drama family fantasy history horror musical mystery noir romance science-fiction thriller war western. A project may name others.

n source deliverable take model platform prompt negative params inputs[] output sha256 costUsd durationMs elapsedMs at{ms|startMs,endMs} human{selectedFrom,promptEditedBy,manualEdits[]} status jobId attempts error thumbnail poster proxy waveform mimeType bytes width height fps frameCount sampleRate channels workflow.


One scene of The Cartographer as a story and conform example. The story and selected media are listed below; provider/model definitions for regeneration are intentionally omitted. Model/platform labels in the historical batch are illustrative. The supplied media has the durations recorded in the batches. Shortened hashes are illustrative; real files use complete hashes. B.10 follows the placements.

the-cartographer/
filmopen-project.json
pj_cartographer_john123_v1.json
character/main-hero/
ch_main-hero_30yo_john123_v1.json
ch_main-hero_30yo_maria_v1.json
ch_main-hero_30yo_official.json
cm_ch_main-hero_maria.json
cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1.png
references/kira/
voice-calm.wav
location/archive/
lo_archive_mid-war_john123_v1.json
cs_lo_archive_mid-war_john123_v1_r7c21_john123_1.png
prop/chart/
pr_chart_default_john123_v1.json
style/war-grade/
st_war-grade_default_maria_v1.json
st_war-grade_default_official.json
s1/
se_s1_john123_v1.json
e2/
ep_e2_john123_v1.json
sc5/
sc_5_john123_v1.json
sh_5.1_john123_v1.json
sh_5.2_john123_v1.json
cu_5.1_john123_v1.json
cu_5.2_john123_v1.json
cu_5.3_john123_v1.json
ff_sh_5.1_john123_v1_r8af0_maria_1.png
cl_sh_5.1_john123_v1_r8af0_maria_1.mp4
ff_sh_5.2_john123_v1_r8af0_maria_1.png
cl_sh_5.2_john123_v1_r8af0_maria_1.mp4
vo_cu_5.1_john123_v1_r8af0_maria_1.wav
am_cu_5.2_john123_v1_r8af0_maria_1.wav
vo_cu_5.3_john123_v1_r8af0_maria_1.wav
render/
rd_7c21_john123.json
rd_8af0_maria.json

Here the media sits beside the JSON, so the manifest names no data locator and the project folder is its own media root:

{ "filmopen": 1, "tag": "cartographer" }

Had the renders been kept in a synced folder next to the project, the manifest would say "data": { "type": "local", "path": "../the-cartographer-data" } and every .png, .mp4 and .wav above would live there instead, under the same names (§4.4).

{
"filmopen": 1, "type": "pj", "tag": "cartographer", "author": "john123", "v": 1,
"name": "The Cartographer", "kind": "series", "genre": ["drama", "mystery"], "year": 2026,
"logline": "A mapmaker discovers her charts are rewriting the coastline.",
"lang": "pt-BR",
"format": { "aspect": "2.39:1", "fps": 24, "resolution": "1920x804", "color": "rec709" },
"epochs": { "default": { "label": "Present day", "order": 1, "year": 2026 }, "mid-war": { "label": "During the war", "order": 2, "year": 1943 } },
"contributors": { "john123": { "name": "João Silva", "role": "director" }, "maria": { "name": "Maria Costa" } },
"tracks": [
{ "id": "pic", "kind": "av", "name": "Picture" },
{ "id": "dx", "kind": "audio", "name": "Dialogue" },
{ "id": "amb", "kind": "audio", "name": "Ambience" },
{ "id": "mx", "kind": "audio", "name": "Music" }
],
"seasons": ["s1"],
"license": "CC-BY-SA-4.0",
"created": "2026-09-05T18:00:00Z", "updated": "2026-09-06T16:00:00Z"
}

B.3 Character — two versions and the official pointer

Section titled “B.3 Character — two versions and the official pointer”
{
"filmopen": 1, "type": "ch", "tag": "main-hero", "epoch": "30yo", "author": "john123", "v": 1,
"name": "Kira Voss", "kind": "principal", "aliases": ["KIRA"],
"summary": "A cartographer who discovers her maps are rewriting the coastline.",
"appearance": { "age": 30, "gender": "woman", "build": "lean", "skin": "olive",
"hair": { "color": "black", "length": "shoulder", "style": "loose" },
"eyes": { "color": "dark brown" }, "marks": ["burn scar, left forearm"] },
"personality": { "summary": "Guarded, precise.", "speech": "Short sentences." },
"voice": { "description": "low, unhurried, slight rasp", "pitch": "low", "pace": "slow", "lang": "pt-BR",
"refs": ["references/kira/voice-calm.wav"] },
"prompt": { "positive": "30yo woman, olive skin, black shoulder-length hair, lean build, burn scar left forearm",
"negative": "cartoon, plastic skin" },
"created": "2026-09-05T18:04:11Z", "updated": "2026-09-05T19:22:40Z"
}
{
"filmopen": 1, "type": "ch", "tag": "main-hero", "epoch": "30yo", "author": "maria", "v": 1,
"name": "Kira Voss", "kind": "principal", "aliases": ["KIRA"],
"forkedFrom": "ch_main-hero_30yo_john123_v1",
"summary": "A cartographer who discovers her maps are rewriting the coastline.",
"appearance": { "age": 30, "gender": "woman", "build": "lean", "skin": "olive",
"hair": { "color": "copper red", "length": "short", "style": "cropped, undercut" },
"eyes": { "color": "dark brown" }, "marks": ["burn scar, left forearm", "small compass-rose tattoo, right wrist"] },
"personality": { "summary": "Guarded, precise.", "speech": "Short sentences." },
"voice": { "description": "low, unhurried, slight rasp", "pitch": "low", "pace": "slow", "lang": "pt-BR",
"refs": ["references/kira/voice-calm.wav"] },
"prompt": { "positive": "30yo woman, olive skin, cropped copper-red hair with undercut, lean build, burn scar left forearm, small compass tattoo right wrist",
"negative": "cartoon, plastic skin" },
"picks": { "sheet": "cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1" },
"refs": { "sheets": ["cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1"] },
"preview": "cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1",
"created": "2026-09-06T09:12:00Z", "updated": "2026-09-06T10:31:02Z"
}
{ "filmopen": 1, "type": "ch", "tag": "main-hero", "epoch": "30yo",
"official": "ch_main-hero_30yo_maria_v1",
"setBy": "john123", "setAt": "2026-09-06T14:20:00Z", "note": "The red hair reads better against the archive." }
{
"filmopen": 1, "type": "lo", "tag": "archive", "epoch": "mid-war", "author": "john123", "v": 1,
"name": "The municipal archive", "aliases": ["ARCHIVE"], "kind": "interior",
"geography": { "city": "Lisbon", "country": "PT" }, "era": "1930s building, wartime neglect",
"description": "Long reading room, iron shelving to the ceiling, one skylight, dust in the air.",
"dressing": ["oak reading table", "green-shade lamps", "map drawers", "sandbags at the windows"],
"lighting": { "default": "single skylight, cold; lamps warm and low", "practicals": ["green-shade desk lamps"] },
"sound": { "roomTone": "large, dead, high ceiling", "ambience": "rain on skylight, distant sirens" },
"prompt": { "positive": "1930s municipal archive reading room, iron shelving, skylight, green desk lamps, sandbagged windows, wartime, dust" },
"picks": { "sheet": "cs_lo_archive_mid-war_john123_v1_r7c21_john123_1" },
"refs": { "sheets": ["cs_lo_archive_mid-war_john123_v1_r7c21_john123_1"] },
"created": "2026-09-05T18:30:00Z", "updated": "2026-09-05T18:30:00Z"
}
{
"filmopen": 1, "type": "pr", "tag": "chart", "epoch": "default", "author": "john123", "v": 1,
"name": "The 1994 harbour chart", "kind": "document", "hero": true,
"description": "Hand-drawn nautical chart, yellowed linen-backed paper, sepia ink, folded twice.",
"prompt": { "positive": "hand-drawn nautical chart, yellowed linen paper, sepia ink, fold creases" },
"created": "2026-09-05T18:40:00Z", "updated": "2026-09-05T18:40:00Z"
}
{
"filmopen": 1, "type": "st", "tag": "war-grade", "epoch": "default", "author": "maria", "v": 1,
"name": "War — gritty, desaturated",
"look": { "stock": "16mm colour negative", "grain": "medium", "contrast": "high", "saturation": "low" },
"palette": ["#2b2f33", "#6b7076", "#b9bcb8", "#d9c9a3"],
"prompt": { "positive": "desaturated, cold grey palette, high contrast, 16mm grain, handheld", "negative": "saturated, glossy" },
"cdl": { "slope": [0.95, 0.98, 1.05], "offset": [-0.02, -0.02, 0.0], "power": [1.1, 1.1, 1.05], "saturation": 0.6 },
"created": "2026-09-06T08:00:00Z", "updated": "2026-09-06T08:00:00Z"
}
{ "filmopen": 1, "type": "st", "tag": "war-grade", "epoch": "default",
"official": "st_war-grade_default_maria_v1", "setBy": "john123" }
{ "filmopen": 1, "type": "se", "tag": "s1", "author": "john123", "v": 1,
"name": "Season 1", "episodes": ["e2"],
"created": "2026-09-05T18:00:00Z", "updated": "2026-09-05T18:00:00Z" }
{
"filmopen": 1, "type": "ep", "tag": "e2", "author": "john123", "v": 1,
"name": "Episode 2 — Low Tide", "synopsis": "Kira finds the first altered chart.",
"epoch": "mid-war",
"cast": [{ "character": "main-hero", "epoch": "30yo" }],
"locations": ["archive"], "styles": ["war-grade"],
"scenes": ["5"],
"created": "2026-09-05T18:00:00Z", "updated": "2026-09-06T12:00:00Z"
}
{
"filmopen": 1, "type": "sc", "tag": "5", "author": "john123", "v": 1,
"name": "The archive",
"location": "archive", "epoch": "mid-war", "storyDay": 3, "time": "night", "weather": "rain",
"cast": [{ "character": "main-hero", "epoch": "30yo" }],
"props": ["chart"], "style": "war-grade",
"synopsis": "Kira compares the two charts and realises the coastline moved.",
"purpose": "First proof. Turns the mystery from doubt to certainty.",
"targetDurationSec": 40,
"shots": ["5.1", "5.2"],
"cues": ["5.1", "5.2", "5.3"],
"blocks": [
{ "id": "bl_1", "kind": "action", "text": "Kira spreads both charts across the reading table." },
{ "id": "bl_2", "kind": "dialogue", "character": "main-hero", "text": "The bay was here last spring.",
"direction": "flat disbelief, does not look up" },
{ "id": "bl_3", "kind": "action", "text": "Behind her, the window flashes white. A second later, the glass goes." },
{ "id": "bl_4", "kind": "dialogue", "character": "main-hero", "text": "Not now.", "offscreen": true }
],
"created": "2026-09-06T11:58:02Z", "updated": "2026-09-06T12:02:11Z"
}

B.7 Shots — eight seconds, then a straight cut to five seconds

Section titled “B.7 Shots — eight seconds, then a straight cut to five seconds”
{
"filmopen": 1, "type": "sh", "tag": "5.1", "author": "john123", "v": 1,
"name": "Kira at the table", "scene": "sc_5_john123_v1", "blocks": ["bl_1", "bl_2"],
"characters": ["main-hero"], "props": ["chart"],
"size": "mcu", "angle": "eye", "movement": "push-in", "movementNotes": "slow, ends tight on her hands",
"lens": { "focalMm": 50, "dof": "shallow" },
"lighting": { "key": { "direction": "side", "quality": "soft" }, "style": "low-key", "colorTempK": 3200, "practicals": ["green desk lamp"] },
"durationSec": 8,
"deliverables": {
"firstFrame": { "model": "nanobanana", "prompt": "Kira leaning over two nautical charts on an oak table, green desk lamp, archive shelving behind, 16mm grain",
"refs": ["cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1", "cs_lo_archive_mid-war_john123_v1_r7c21_john123_1"] },
"clip": { "model": "seedance25", "prompt": "slow push in as she traces the coastline with a finger and speaks without looking up", "from": "firstFrame" }
},
"place": { "track": "pic", "with": "scene", "offsetMs": 0 },
"picks": { "firstFrame": "ff_sh_5.1_john123_v1_r8af0_maria_1", "clip": "cl_sh_5.1_john123_v1_r8af0_maria_1" },
"created": "2026-09-06T12:10:00Z", "updated": "2026-09-06T16:00:00Z"
}
{
"filmopen": 1, "type": "sh", "tag": "5.2", "author": "john123", "v": 1,
"name": "The window goes", "scene": "sc_5_john123_v1", "blocks": ["bl_3", "bl_4"],
"characters": ["main-hero"],
"size": "ms", "angle": "low", "movement": "static",
"lens": { "focalMm": 35, "dof": "moderate" },
"lighting": { "key": { "direction": "back", "quality": "hard" }, "style": "low-key", "notes": "single white flash from behind, then darkness with lamp only" },
"continuity": ["charts still on the table", "scar visible, left forearm"],
"vfx": "glass shatter, no debris toward lens",
"durationSec": 5,
"deliverables": {
"firstFrame": { "model": "nanobanana", "prompt": "low angle, Kira in medium shot at the archive table, the window behind her blown white", "refs": ["cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1"] },
"clip": { "model": "seedance25", "prompt": "the window flashes white, glass falls, she does not turn", "from": "firstFrame" }
},
"place": { "track": "pic", "after": "sh_5.1", "offsetMs": 0, "transitionIn": { "type": "cut" } },
"picks": { "firstFrame": "ff_sh_5.2_john123_v1_r8af0_maria_1", "clip": "cl_sh_5.2_john123_v1_r8af0_maria_1" },
"created": "2026-09-06T12:12:00Z", "updated": "2026-09-06T16:00:00Z"
}

B.8 Cues — dialogue riding with picture, a J-cut leading the second shot, ambience under the scene

Section titled “B.8 Cues — dialogue riding with picture, a J-cut leading the second shot, ambience under the scene”
{
"filmopen": 1, "type": "cu", "tag": "5.1", "author": "john123", "v": 1,
"name": "Kira — the bay line", "kind": "dialogue", "scene": "sc_5_john123_v1", "blocks": ["bl_2"],
"character": "main-hero",
"delivery": { "effort": "quiet", "emotion": "flat disbelief" },
"model": "elevenlabs",
"place": { "track": "dx", "with": "sh_5.1", "offsetMs": 2750, "gainDb": -3 },
"picks": { "voice": "vo_cu_5.1_john123_v1_r8af0_maria_1" },
"created": "2026-09-06T12:20:00Z", "updated": "2026-09-06T16:00:00Z"
}
{
"filmopen": 1, "type": "cu", "tag": "5.3", "author": "john123", "v": 1,
"name": "Kira — not now (J-cut)", "kind": "dialogue", "scene": "sc_5_john123_v1", "blocks": ["bl_4"],
"character": "main-hero",
"delivery": { "effort": "quiet", "emotion": "clipped" },
"model": "elevenlabs",
"place": { "track": "dx", "with": "sh_5.2", "offsetMs": -1500, "gainDb": -3 },
"picks": { "voice": "vo_cu_5.3_john123_v1_r8af0_maria_1" },
"notes": "Her line starts 1.5 s before the picture cuts to 5.2 — we hear her before we see the window go.",
"created": "2026-09-06T12:22:00Z", "updated": "2026-09-06T16:00:00Z"
}
{
"filmopen": 1, "type": "cu", "tag": "5.2", "author": "john123", "v": 1,
"name": "Archive — rain and sirens", "kind": "ambience", "scene": "sc_5_john123_v1", "blocks": ["bl_1", "bl_2", "bl_3", "bl_4"],
"sfx": { "description": "rain on a skylight, large dead room, one distant siren rising and falling" },
"model": "elevenlabs-sfx",
"place": { "track": "amb", "with": "scene", "offsetMs": 0, "inMs": 0, "outMs": 13000, "fadeInMs": 1000, "fadeOutMs": 1500, "gainDb": -18 },
"picks": { "ambience": "am_cu_5.2_john123_v1_r8af0_maria_1" },
"created": "2026-09-06T12:25:00Z", "updated": "2026-09-06T16:00:00Z"
}
{ "filmopen": 1, "type": "rd", "id": "7c21", "author": "john123", "kind": "manual",
"status": "done", "items": [
{ "n": 1, "source": "lo_archive_mid-war_john123_v1", "deliverable": "sheet",
"output": "location/archive/cs_lo_archive_mid-war_john123_v1_r7c21_john123_1.png" }
] }
{
"filmopen": 1, "type": "rd", "id": "8af0", "author": "maria", "kind": "render", "tier": "final",
"created": "2026-09-06T15:30:00Z", "status": "done",
"items": [
{ "n": 1, "source": "ch_main-hero_30yo_maria_v1", "deliverable": "sheet", "model": "midjourney", "platform": "midjourney",
"prompt": "character sheet, 30yo woman, olive skin, cropped copper-red hair with undercut, lean build, burn scar left forearm, compass tattoo right wrist, front three-quarter profile, neutral grey",
"output": "character/main-hero/cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1.png", "sha256": "a4f1…", "costUsd": 0.08,
"width": 2048, "height": 2048, "human": { "selectedFrom": 6 } },
{ "n": 2, "source": "sh_5.1_john123_v1", "deliverable": "firstFrame", "model": "nanobanana", "platform": "fal",
"inputs": ["character/main-hero/cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1.png", "location/archive/cs_lo_archive_mid-war_john123_v1_r7c21_john123_1.png"],
"output": "s1/e2/sc5/ff_sh_5.1_john123_v1_r8af0_maria_1.png", "sha256": "0c9e…", "costUsd": 0.04, "human": { "selectedFrom": 4 } },
{ "n": 3, "source": "sh_5.1_john123_v1", "deliverable": "clip", "model": "seedance25", "platform": "fal",
"params": { "seed": 41221, "durationSec": 8, "fps": 24 }, "inputs": ["s1/e2/sc5/ff_sh_5.1_john123_v1_r8af0_maria_1.png"],
"output": "s1/e2/sc5/cl_sh_5.1_john123_v1_r8af0_maria_1.mp4", "sha256": "9d02…", "durationMs": 8000, "elapsedMs": 64200, "costUsd": 0.56, "human": { "selectedFrom": 3 } },
{ "n": 4, "source": "sh_5.2_john123_v1", "deliverable": "firstFrame", "model": "nanobanana", "platform": "fal",
"output": "s1/e2/sc5/ff_sh_5.2_john123_v1_r8af0_maria_1.png", "sha256": "5b77…", "costUsd": 0.04 },
{ "n": 5, "source": "sh_5.2_john123_v1", "deliverable": "clip", "model": "seedance25", "platform": "fal",
"params": { "seed": 41222, "durationSec": 5, "fps": 24 }, "inputs": ["s1/e2/sc5/ff_sh_5.2_john123_v1_r8af0_maria_1.png"],
"output": "s1/e2/sc5/cl_sh_5.2_john123_v1_r8af0_maria_1.mp4", "sha256": "e310…", "durationMs": 5000, "elapsedMs": 41800, "costUsd": 0.35 },
{ "n": 6, "source": "cu_5.1_john123_v1", "deliverable": "voice", "model": "elevenlabs", "platform": "elevenlabs",
"output": "s1/e2/sc5/vo_cu_5.1_john123_v1_r8af0_maria_1.wav", "sha256": "31cd…", "durationMs": 2500, "sampleRate": 48000, "channels": 1, "costUsd": 0.01 },
{ "n": 7, "source": "cu_5.3_john123_v1", "deliverable": "voice", "model": "elevenlabs", "platform": "elevenlabs",
"output": "s1/e2/sc5/vo_cu_5.3_john123_v1_r8af0_maria_1.wav", "sha256": "c0de…", "durationMs": 1000, "sampleRate": 48000, "channels": 1, "costUsd": 0.01 },
{ "n": 8, "source": "cu_5.2_john123_v1", "deliverable": "ambience", "model": "elevenlabs-sfx", "platform": "elevenlabs",
"output": "s1/e2/sc5/am_cu_5.2_john123_v1_r8af0_maria_1.wav", "sha256": "88a2…", "durationMs": 14000, "costUsd": 0.02 }
]
}
{
"filmopen": 1, "type": "cm", "target": "ch_main-hero", "author": "maria",
"entries": [
{ "on": "ch_main-hero_30yo_john123_v1", "at": "2026-09-06T09:05:00Z", "rating": 1, "text": "Good bones. Hair is too safe for the archive — trying red." },
{ "on": "cs_ch_main-hero_30yo_maria_v1_r8af0_maria_1", "at": "2026-09-06T15:40:00Z", "rating": 1 }
]
}

One timeline, Episode 2, 24 fps, 13 seconds (312 frames). All boundaries in this example are frame-aligned, so no timing rounding is needed:

Track Clip Start End
V1 Picture cl_sh_5.1… 0:00.000 0:08.000
V1 Picture cl_sh_5.2… 0:08.000 0:13.000
A1 Picture the two clips’ embedded sound as above
A2 Dialogue vo_cu_5.1… 0:02.750 0:05.250
A2 Dialogue vo_cu_5.3… 0:06.500 0:07.500 — leads the cut by 1.5 s
A3 Ambience am_cu_5.2… 0:00.000 0:13.000, 1 s in, 1.5 s out

A marker sc_5 at 0:00.000; a marker on each clip carrying its stem; conform/ holding sh_5.1_clip.mp4, sh_5.2_clip.mp4, cu_5.1_voice.wav, cu_5.3_voice.wav, cu_5.2_ambience.wav; a manifest; and a report noting that the ambience take is 14 s and is trimmed to 13.


Appendix C — MovieLabs OMC correspondence

Section titled “Appendix C — MovieLabs OMC correspondence”

Concept-level. Property names must be confirmed against the OMC-JSON v2.8 schema before an exporter is written.

FilmOpen OMC concept Notes
pj Creative Work title, synopsis; contributors and credits → Participants
ch Character name; appearance → profile / physical characteristics; aliases → alternate names
of Narrative Wardrobe / Costume linked to Character
lo Narrative Location areas → contained Locations
pr Narrative Prop hero → notable
st no OMC equivalent; export as Asset with CDL sidecar
se ep sq Creative Work structure / Sequence nested by scenes, sequences, episodes
sc Narrative Scene blocks → scene description and dialogue; storyDay → Context
sh Shot place → editorial timing; with Slate where an import batch supplies one
take Asset media file with identifiers; sha256 → identifier
picks Depiction the narrative-object-to-asset link
rd Task and Asset provenance human → participant contribution
author Participant owner segment
epoch Context “in which circumstances”

FilmOpen Project Specification 1.3 Draft · filmopen.ai · CC-BY-4.0