# Convert a screenplay file to ScreenJSON Source: https://docs.screenjson.com/api-reference/cli/endpoint/conversion/convert-a-screenplay-file-to-screenjson /api-reference/cli/openapi.json post /convert Upload a screenplay in a supported writer format. The server returns the resulting ScreenJSON document as JSON. Accepts either **multipart/form-data** with a `file` field, or a raw body with the format specified via query parameter. Max request body: **50 MB**. # Export a ScreenJSON document to a screenplay format Source: https://docs.screenjson.com/api-reference/cli/endpoint/conversion/export-a-screenjson-document-to-a-screenplay-format /api-reference/cli/openapi.json post /export POST a ScreenJSON document (JSON or YAML) and receive the exported screenplay as a binary download. The response's `Content-Type` and `Content-Disposition` are set based on the requested output format. Max request body: **50 MB**. # Validate a ScreenJSON document Source: https://docs.screenjson.com/api-reference/cli/endpoint/conversion/validate-a-screenjson-document /api-reference/cli/openapi.json post /validate POST a ScreenJSON document (JSON). Returns `{ valid: true }` on success, or `{ valid: false, errors: [{ path, message }] }` on failure. Max request body: **10 MB**. # Health check Source: https://docs.screenjson.com/api-reference/cli/endpoint/system/health-check /api-reference/cli/openapi.json get /health Returns overall service health plus per-dependency status for any configured external services (Gotenberg, Tika, LLM). # List supported formats Source: https://docs.screenjson.com/api-reference/cli/endpoint/system/list-supported-formats /api-reference/cli/openapi.json get /formats Returns every format understood by the running binary, with its file extensions, MIME types, and whether it can be used as an input, an output, or both. # Status and metrics Source: https://docs.screenjson.com/api-reference/cli/endpoint/system/status-and-metrics /api-reference/cli/openapi.json get / Returns a service descriptor with the current queue depth, worker count, and a catalogue of exposed endpoints. # Introduction Source: https://docs.screenjson.com/api-reference/cli/introduction The REST API exposed by screenjson-cli when run in server mode. `screenjson serve` starts an HTTP server that wraps the same conversion, export, and validation engine as the CLI. Use it from applications, serverless functions, or a frontend that needs to convert a screenplay without shelling out. ## Start a server ```bash theme={null} screenjson serve --host 0.0.0.0 --port 8080 ``` Or with Docker: ```bash theme={null} docker run --rm -p 8080:8080 screenjson/cli serve --port 8080 ``` See [`screenjson serve`](/tools/cli/serve) for the full flag list. ## Base URLs | Environment | URL | | - | - | | Local | `http://localhost:8080` | | Public demo | `https://api.try.screenjson.com` | ## Endpoints at a glance | Method | Path | Purpose | | - | - | - | | `GET` | `/` | Service descriptor, queue metrics, endpoint catalogue. | | `GET` | `/health` | Health check with per-service status. | | `GET` | `/formats` | List every supported format. | | `POST` | `/convert` | Upload a file, receive ScreenJSON. | | `POST` | `/export` | POST a ScreenJSON document, receive an exported file. | | `POST` | `/validate` | POST a ScreenJSON document, receive a validation report. | Detail pages for every endpoint are generated from the [OpenAPI spec](https://github.com/screenjson/docs/blob/main/api-reference/cli/openapi.json) — see the **screenjson-cli endpoints** group in this sidebar. ## Request limits * `/convert` and `/export`: **50 MB** request body. * `/validate`: **10 MB** request body. ## CORS The server emits a permissive policy: ``` Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization ``` ## Error shape All 4xx / 5xx responses are JSON: ```json theme={null} { "error": "human-readable message" } ``` ## Authentication The vanilla server exposes no authentication. For production deployments, put it behind an API gateway (AWS API Gateway, Cloudflare, nginx) and enforce auth there, or use [Greenlight](/tools/greenlight/overview) which bundles bearer token auth. # Server configuration snapshot Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/config/server-configuration-snapshot /api-reference/greenlight/openapi.json get /setup # Server log tail Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/debug/server-log-tail /api-reference/greenlight/openapi.json get /tail # Health check Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/health/health-check /api-reference/greenlight/openapi.json get /up # Queue a screenplay conversion job Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/jobs/queue-a-screenplay-conversion-job /api-reference/greenlight/openapi.json post /push # Get a specific job result Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/results/get-a-specific-job-result /api-reference/greenlight/openapi.json get /results/{id} # List recent job results Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/results/list-recent-job-results /api-reference/greenlight/openapi.json get /results # Browse S3/MinIO storage Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/storage/browse-s3minio-storage /api-reference/greenlight/openapi.json get /storage # List available tasks Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/tasks/list-available-tasks /api-reference/greenlight/openapi.json get /tasks # Webhook configuration Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/webhooks/webhook-configuration /api-reference/greenlight/openapi.json get /webhooks # Live worker status Source: https://docs.screenjson.com/api-reference/greenlight/endpoint/workers/live-worker-status /api-reference/greenlight/openapi.json get /workers # Introduction Source: https://docs.screenjson.com/api-reference/greenlight/introduction The Greenlight REST API. Greenlight exposes a REST API on port `8080` (mapped to `19000` in the default docker-compose). Use it to submit jobs, list results, check worker status, and configure webhooks. ## Base URLs | Environment | URL | | - | - | | Local (backend direct) | `http://localhost:8080` | | Docker Compose (mapped) | `http://localhost:19000` | | Production | whatever you've deployed — typically behind a reverse proxy | ## Endpoints at a glance | Method | Path | Purpose | | - | - | - | | `GET` | `/up` | Liveness probe. | | `GET` | `/setup` | Configuration snapshot (safe subset). | | `POST` | `/push` | Submit a conversion job / pipeline. | | `GET` | `/results` | List recent job results. | | `GET` | `/results/{id}` | Retrieve a specific job result. | | `GET` | `/tasks` | Return the task catalogue. | | `GET` | `/workers` | Per-worker live status. | | `GET` | `/storage` | Browse the configured object store. | | `GET` | `/webhooks` | Webhook configuration + recent deliveries. | | `GET` | `/tail` | Recent server log lines. | Detail pages for every endpoint are generated from the [OpenAPI spec](https://github.com/screenjson/docs/blob/main/api-reference/greenlight/openapi.json) — see the **Greenlight endpoints** group in this sidebar. ## Authentication Three optional schemes: * **Basic auth** — set `GREENLIGHT_USERNAME` / `GREENLIGHT_PASSWORD`. * **Bearer token** — set `SHARED_API_SECRET=1` and `SHARED_API_KEY=`; clients send `Authorization: Bearer `. * **Open** — leave all of the above empty. Useful behind a trusted network gateway. ## Content-Type All POST payloads are `application/json` unless otherwise noted. ## Error shape 4xx / 5xx responses are JSON with at least: ```json theme={null} { "error": "human-readable message" } ``` Some endpoints additionally return validation failure detail. ## Rate limiting The built-in server does not rate-limit. Enforce limits at the reverse proxy layer. Internal worker concurrency is governed by `MAX_WORKERS` (default `4` per replica). ## Idempotency The `/push` endpoint is **not** idempotent. Submitting the same payload twice enqueues two jobs. Use `document_id` to correlate server-side if you need dedup semantics. # Analysis Source: https://docs.screenjson.com/concepts/analysis Optional derived data — embeddings, passages, summaries — attached without mutating the canonical document. The `analysis` block carries derived data for retrieval and AI workflows. It is **optional** and **discardable**: a document without analysis is still canonical. ## Shape ```json theme={null} { "analysis": { "embeddings": { "": [ /* embeddings */ ] }, "passages": [ /* passages */ ], "summaries": [ /* summaries */ ], "settings": { /* how this was produced */ } } } ``` ## `analysis.embeddings` A map keyed by the **UUID of the target** (scene, element, or character), whose value is an array of one or more embedding records: ```json theme={null} { "id": "", "model": "text-embedding-3-large", "dimensions": 1536, "values": [0.023, -0.041, ...], "source": "text", "lang": "en", "tokens": 420, "created": "2026-01-14T10:30:00Z" } ``` **`source`** enum: `name` · `text` · `desc` · `heading` · `composite`. ## `analysis.passages` Retrieval-sized text chunks keyed back to their source: ```json theme={null} { "id": "", "scene": "", "elements": ["", ""], "text": { "en": "..." }, "tokens": 256, "overlap": 32 } ``` ## `analysis.summaries` Document- or scene-scoped summaries: ```json theme={null} { "id": "", "scope": "scene", "target": "", "generated": true, "model": "claude-opus-4-7", "text": { "en": "..." }, "created": "2026-01-14T10:45:00Z" } ``` **`scope`** enum: `document` · `scene`. ## `analysis.settings` Records how the analysis was produced, for reproducibility: | Field | Type | Description | | - | - | - | | `model` | string (≤100) | Embedding or summary model identifier. | | `size` | integer ≥1 | Chunk size. Default `512`. | | `overlap` | integer ≥0 | Chunk overlap in tokens. Default `64`. | | `tokeniser` | string (≤50) | Tokeniser identifier (e.g. `cl100k`). | | `meta` | meta map | Free-form string key/value extension. | # Encryption Source: https://docs.screenjson.com/concepts/encryption AES-256-CTR encryption of text runs. Structure, UUIDs, and metadata stay visible. ScreenJSON supports confidentiality without sacrificing structural introspection: **only text runs are encrypted**. UUIDs, timestamps, scene counts, character lists, revision labels, and `meta` stay in plain text. ## The `encrypt` block ```json theme={null} { "encrypt": { "cipher": "aes-256-ctr", "hash": "sha256", "encoding": "hex" } } ``` | Field | Default | Values | | - | - | - | | `cipher` | `aes-256-ctr` | Any symmetric cipher string the tooling supports. | | `hash` | `sha256` | Hash used for key derivation. | | `encoding` | `hex` | `hex` · `base16` · `base32` · `base64` · `ascii85`. | | `meta` | `{}` | Free-form extension. | ## Key derivation The encryption key is derived from a shared secret via a single pass of the declared `hash` (default SHA-256). The key must be at least **10 characters** long. ## Scope * **Document-level** `encrypt` (on the document root) applies to every element that doesn't set its own. * **Element-level** `encrypt` on an individual element overrides the root. ## What gets encrypted * Every value in a `text` language map on every element. * Every value in `note.text`, `logline`, cover `extra`, character `desc`, slugline `desc`, scene-level summary text. ## What does not * UUIDs (document, scene, element, character, note, revision, bookmark). * Timestamps (`created`, `modified`). * Tag arrays (`props`, `sfx`, `locations`, `genre`, `themes`, etc.). * Character canonical `name` and `aliases`. * `meta` maps. * Everything under `analysis` (embeddings, passages, summaries). ## CLI entry points * [`screenjson encrypt`](/tools/cli/encrypt) — full-document encryption pass. * [`screenjson decrypt`](/tools/cli/decrypt) — matching decrypt. * [`screenjson convert --encrypt `](/tools/cli/convert) — encrypt at convert time. * [`screenjson export --decrypt `](/tools/cli/export) — decrypt at export time. # Language maps Source: https://docs.screenjson.com/concepts/language-maps Every user-facing text field in ScreenJSON is keyed by BCP 47 language tag. Every user-facing text field in ScreenJSON is a **map** keyed by BCP 47 language tag, not a plain string. ```json theme={null} { "text": { "en": "We have ninety seconds.", "fr": "Nous avons quatre-vingt-dix secondes.", "ja": "九十秒しかない。" } } ``` ## Two map types | Type | Max length per value | Where it's used | | - | - | - | | `name` | 255 characters | Short identifiers — titles, display names, cover labels. | | `text` | 10,000 characters | Long content — dialogue, action, descriptions, notes. | ## Key pattern Both types use the BCP 47 regex from the schema: ``` ^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$ ``` Valid examples: `en`, `en-GB`, `fr-CA`, `zh-Hant`, `sr-Latn-RS`. ## Picking text at read time ```ts theme={null} function pickText( map: Record, preferred: string, fallback = 'en', ): string { return map[preferred] ?? map[fallback] ?? Object.values(map)[0] ?? ''; } ``` ## Where maps appear * Root: `title`, `logline` * Cover: `title`, `extra` * Character: `desc` * Source: `title` * Slugline: `desc` * Element: `text` on `action`, `dialogue`, `parenthetical`, `shot`, `transition`, `general` * Note: `text` * Bookmark: `title`, `desc` * Ribbon: `text` * Summary / passage: `text` # The ScreenJSON format Source: https://docs.screenjson.com/concepts/screenjson A one-page orientation to the shape of a ScreenJSON document. A ScreenJSON file is a JSON document with a stable top-level shape. The authoritative machine-readable definition is the [JSON Schema](https://screenjson.com/schema.json) at `https://screenjson.com/schema.json`. ## Top-level shape ```json theme={null} { "id": "", "version": "1.0.0", "title": { "en": "The Heist" }, "lang": "en", "charset": "utf-8", "dir": "ltr", "authors": [ /* one or more */ ], "document": { "cover": { ... }, "scenes": [ ... ] } } ``` ## Required root properties | Property | Type | Description | | - | - | - | | `id` | uuid | RFC 4122 document identifier. | | `version` | semver string | ScreenJSON schema version the document was authored against. | | `title` | language map | Title keyed by BCP 47 language tag. | | `lang` | BCP 47 | Primary language. | | `charset` | string | IANA character set label. Default `utf-8`. | | `dir` | `ltr` \| `rtl` | Text direction. | | `authors` | array | One or more author entries. | | `document` | object | The screenplay container — cover, scenes, optional layout and bookmarks. | ## Optional root properties `generator`, `locale`, `contributors`, `characters`, `colors`, `sources`, `registrations`, `revisions`, `encrypt`, `license`, `taggable`, `genre`, `themes`, `logline`, `analysis`. See [specification/document-root](/specification/document-root) for the complete description. ## Commitments * **Structural, not presentational.** Slug, action, cue, parenthetical, dialogue, shot, transition are typed nodes — not layout hints. Rendering lives in the optional `layout` block. * **Language as first-class.** Every user-facing text field is a BCP 47 keyed map. * **Stable identity.** Every referenceable node carries a UUID. * **Extensible without ambiguity.** Extension data lives only in `meta`; core fields are `additionalProperties: false`. * **Analysis is additive.** Embeddings, passages, summaries live under `analysis` and are discardable. # UUIDs and identity Source: https://docs.screenjson.com/concepts/uuids Every node that can be referenced from elsewhere carries an RFC 4122 UUID. Every node in a ScreenJSON document that can be referenced from elsewhere carries an [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122) UUID. ## Format ``` ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$ ``` Example: `7a1c0b4e-8f3a-4c68-9d2e-ac8f21a0d8a4`. ## Nodes with UUIDs | Node | Where | | - | - | | Document | root `id` | | Author | `authors[].id` | | Contributor | `contributors[].id` | | Character | `characters[].id` | | Scene | `document.scenes[].id` | | Element | `document.scenes[].body[].id` | | Note | `notes[].id` | | Revision | `revisions[].id` | | Bookmark | `document.bookmarks[].id` | | Source | `sources[].id` | | Embedding | `analysis.embeddings[][].id` | | Passage | `analysis.passages[].id` | | Summary | `analysis.summaries[].id` | ## Cross-references * `dialogue.character` and `character` (cue).character point to `characters[].id`. * `note.contributor` points to `contributors[].id`. * `note.parent` points to the element, scene, or note the annotation is attached to. * `bookmark.scene` and `bookmark.element` point to their targets. * `passage.scene` and `passage.elements[]` point to the elements a passage covers. * `summary.target` points to a scene (when `scope = "scene"`). * `element.scene` back-references its parent scene. * `revision.parent` points to the revision it was forked from. ## Guarantees * A given UUID identifies **one** node for the lifetime of the document. * Regenerating output across a compatible change SHOULD reuse the same UUIDs, not mint new ones. Both `screenjson convert` and `screenjson-export` honour this when the output file already exists on disk. # ScreenJSON Documentation Source: https://docs.screenjson.com/index Reference documentation for the ScreenJSON toolchain — schema, CLI tools, viewer, and Greenlight batch microservice. ScreenJSON is an open schema for screenplay data and a set of tools that produce, consume, render, and manage it. These docs are **reference**, grounded in what each tool actually does. Every flag, environment variable, task, and endpoint below matches the released source. For narrative articles, audience guides, and how-tos, see [screenjson.com](https://screenjson.com). ## Tools Free, open-source CLI. FDX / Fountain / FadeIn → ScreenJSON. MIT-licensed. The full CLI. All formats in and out, validation, encryption, REST API. Embeddable JavaScript viewer. CDN or npm, plain HTML, React, Svelte. Batch microservice. Job queue, S3 ingestion, pipeline tasks, webhooks. ## Try it now Read any ScreenJSON document in the browser — drop a file, paste a URL, or deep-link with ?src=. Powered by [screenjson-ui](/tools/ui/overview). ## API Reference REST endpoints exposed by `screenjson serve`. Job submission, results, workers, storage, tasks. ## Specification The authoritative reference for the document model, shared types, element types, layout, and analysis. # Install Source: https://docs.screenjson.com/install Installation instructions for every ScreenJSON tool. ## screenjson-export Free, open-source reference CLI. FDX / Fountain / FadeIn → ScreenJSON. Latest: [v1.0.0](https://github.com/screenjson/screenjson-export/releases/tag/v1.0.0). ```bash theme={null} curl -sL https://github.com/screenjson/screenjson-export/releases/download/v1.0.0/screenjson-export_$(uname -s)_$(uname -m).tar.gz | tar -xz sudo mv screenjson-export /usr/local/bin/ ``` ```bash theme={null} docker pull screenjson/export:latest ``` ```bash theme={null} go install screenjson/export/cmd/screenjson-export@latest ``` ```bash theme={null} git clone https://github.com/screenjson/screenjson-export.git cd screenjson-export go build -trimpath -o screenjson-export ./cmd/screenjson-export sudo mv screenjson-export /usr/local/bin/ ``` ## screenjson-cli Commercial, full-featured CLI. ```bash theme={null} docker pull screenjson/cli:latest docker run --rm screenjson/cli help ``` ```bash theme={null} git clone https://github.com/screenjson/screenjson-cli.git cd screenjson-cli go build -o screenjson ./cmd/screenjson sudo mv screenjson /usr/local/bin/ ``` PDF import additionally requires [Poppler](https://poppler.freedesktop.org/): ```bash theme={null} # macOS brew install poppler # Debian / Ubuntu sudo apt-get install poppler-utils ``` ## screenjson-ui Embeddable browser viewer. ```bash theme={null} npm install screenjson-ui ``` ```html theme={null} ``` ## Greenlight Batch microservice. See [Greenlight → Deploy](/tools/greenlight/deploy) for the full instructions. ```bash theme={null} git clone https://github.com/screenjson/greenlight.git cd greenlight cp env.example backend/.env docker build -f deployment/Dockerfile -t screenjson/greenlight:latest . cd deployment && docker-compose up ``` # Quickstart Source: https://docs.screenjson.com/quickstart Convert a screenplay to ScreenJSON in under a minute. ## 1. Install the free reference CLI Download a platform binary from the [releases page](https://github.com/screenjson/screenjson-export/releases/tag/v1.0.0): * macOS · Apple Silicon — `screenjson-export_Darwin_arm64.tar.gz` * macOS · Intel — `screenjson-export_Darwin_x86_64.tar.gz` * Linux · amd64 — `screenjson-export_Linux_x86_64.tar.gz` * Linux · arm64 — `screenjson-export_Linux_arm64.tar.gz` * Windows · amd64 — `screenjson-export_Windows_x86_64.zip` * Windows · arm64 — `screenjson-export_Windows_arm64.zip` ```bash theme={null} tar -xzf screenjson-export_*.tar.gz sudo mv screenjson-export /usr/local/bin/ ``` ```bash theme={null} docker pull screenjson/export:latest ``` ```bash theme={null} git clone https://github.com/screenjson/screenjson-export.git cd screenjson-export go build -o screenjson-export ./cmd/screenjson-export sudo mv screenjson-export /usr/local/bin/ ``` ## 2. Convert a file ```bash theme={null} screenjson-export -i screenplay.fdx -o screenplay.json ``` Or in Docker: ```bash theme={null} docker run --rm -v "$PWD:/data" screenjson/export -i /data/screenplay.fdx ``` Fountain and FadeIn are auto-detected from the file extension: ```bash theme={null} screenjson-export -i script.fountain -o script.json screenjson-export -i script.fadein -o script.json ``` ## 3. Inspect the output ```bash theme={null} screenjson-export -i script.fdx | jq '.title.en' screenjson-export -i script.fdx | jq '.document.scenes | length' screenjson-export -i script.fdx | jq '.characters[].name' ``` ## 4. Read it in the browser The hosted viewer at [screenjson.com/viewer/](https://screenjson.com/viewer/) renders any ScreenJSON document as a Final Draft / WriterDuet-style screenplay. Drop your file in, paste a URL, or deep-link with `?src=`: ``` https://screenjson.com/viewer/?src=https://example.com/screenplay.json ``` ## Next steps Export to every format, run a REST API, and more. Paste a URL or drop a file — convert without installing anything. The shape of a ScreenJSON document. Endpoints exposed by `screenjson serve`. # Analysis Source: https://docs.screenjson.com/specification/analysis Derived retrieval-oriented data attached to a canonical document. The `analysis` block carries derived data for retrieval and AI workflows. Optional and discardable — a document without `analysis` is still canonical. ## Shape ```json theme={null} { "analysis": { "embeddings": { "": [ /* embeddings */ ] }, "passages": [ /* passages */ ], "summaries": [ /* summaries */ ], "settings": { /* how this was produced */ }, "meta": {} } } ``` ## `embeddings` A map keyed by the **UUID of the target** (a scene, element, or character). Value is an array of one or more embedding records. ```json theme={null} { "id": "", "model": "text-embedding-3-large", "dimensions": 1536, "values": [0.023, -0.041, ...], "source": "text", "lang": "en", "tokens": 420, "created": "2026-01-14T10:30:00Z" } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | Embedding identifier. | | `model` | string (1–100) | ✓ | Model identifier. | | `dimensions` | integer 1–4096 | ✓ | Vector length. | | `values` | number\[] 1–4096 | ✓ | Vector payload. | | `source` | enum | ✓ | Which field was embedded. `name`, `text`, `desc`, `heading`, `composite`. | | `created` | datetime | ✓ | ISO 8601. | | `lang` | lang | — | BCP 47 tag the source text was drawn from. | | `tokens` | integer ≥1 | — | Token count of the source text. | | `meta` | meta | — | Extension. | Keys in the `embeddings` map are matched against the UUID pattern — only valid RFC 4122 strings are accepted. ## `passages` Retrieval-sized text chunks keyed to source elements. ```json theme={null} { "id": "", "scene": "", "elements": ["", ""], "text": { "en": "..." }, "tokens": 256, "overlap": 32 } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | | | `scene` | uuid | ✓ | | | `elements` | uuid\[] (≥1) | ✓ | Element ids covered by this passage. | | `text` | [text](/specification/shared-types#text) | ✓ | Passage text, language-keyed. | | `tokens` | integer ≥1 | ✓ | | | `overlap` | integer ≥0 | — | Sliding-window overlap in tokens. Default `0`. | | `meta` | meta | — | Extension. | ## `summaries` Document- or scene-scoped summaries. ```json theme={null} { "id": "", "scope": "scene", "target": "", "generated": true, "model": "claude-opus-4-7", "text": { "en": "..." }, "created": "2026-01-14T10:45:00Z" } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | | | `scope` | enum | ✓ | `document` or `scene`. | | `text` | [text](/specification/shared-types#text) | ✓ | Summary text. | | `created` | datetime | ✓ | ISO 8601. | | `target` | uuid \| null | — | Scene id when `scope="scene"`. | | `generated` | boolean | — | `true` for machine-generated. Default `false`. | | `model` | string (≤100) | — | Producing model id. | | `meta` | meta | — | Extension. | ## `settings` Records how derived data was produced — for reproducibility. | Field | Type | Default | Description | | - | - | - | - | | `model` | string (≤100) | — | Embedding or summary model. | | `size` | integer ≥1 | `512` | Chunk size. | | `overlap` | integer ≥0 | `64` | Chunk overlap. | | `tokeniser` | string (≤50) | — | Tokeniser identifier — e.g. `cl100k`. | | `meta` | meta | `{}` | Extension. | # Authors & contributors Source: https://docs.screenjson.com/specification/authors Credit model — original authorship vs. editorial contribution. ScreenJSON separates two kinds of credit. ## `authors` Original writers. Required at the root (at least one) and at every element. ```json theme={null} { "id": "", "given": "Jordan", "family": "Lee", "meta": {} } ``` | Field | Type | Required | Notes | | - | - | - | - | | `id` | uuid | ✓ | Stable across the document. | | `given` | string (1–50) | ✓ | Given name. | | `family` | string (1–50) | ✓ | Family name. | | `meta` | meta | — | Extension — e.g. ORCID, WGA id. | ## `contributors` Anyone who made artistic input **other than** original authorship — editor, director, script doctor, showrunner. Optional. ```json theme={null} { "id": "", "given": "Ali", "family": "Okonkwo", "roles": ["editor", "script-doctor"], "meta": {} } ``` Same fields as author, plus: | Field | Type | Notes | | - | - | - | | `roles` | slug\[] | Unique list of role slugs. | ## Element-level Every element in a scene body carries: * `authors`: uuid\[] — at least one author id from the root `authors` array. * `contributors`: uuid\[] — any contributor ids from the root `contributors`. Attribution at element granularity means revision history and editorial passes can be recorded without ambiguity. # Characters Source: https://docs.screenjson.com/specification/characters The character index referenced by cues and dialogue. Characters are declared once, at the root, and referenced everywhere else by UUID. ## Shape ```json theme={null} { "id": "", "slug": "mara", "name": "MARA", "aliases": ["Mara Halpern", "M."], "desc": { "en": "A thief, mid-30s." }, "traits": ["lead", "thief", "antihero"], "meta": {} } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | Stable character identifier. | | `name` | string (1–80) | ✓ | Canonical display name — the string rendered in character cues (traditionally uppercase). | | `slug` | [slug](/specification/shared-types#slug) | — | Tooling identifier. | | `aliases` | string\[] (unique, 1–80 each) | — | Alternate names used inside the script. | | `desc` | [text](/specification/shared-types#text) | — | Language-keyed description. | | `traits` | slug\[] (unique) | — | Editorial taxonomy — casting types, narrative roles, demographics. Non-canonical. | | `meta` | meta | — | Extension. | ## Canonical vs. alias * `name` is the single string used in character cues. Pattern matching uses this exactly. * `aliases` is an array of alternate strings that should resolve to the same character — typically historical spellings (`M.`) or third-person forms (`Mara Halpern`). ## References Every element that references a character points to `characters[].id`: * `cue.character` — the character speaking. * `dialogue.character` — the character whose line this is. * `scene.cast[]` — every character present in the scene. * `note.contributor` — points at a `contributors[].id`, not a character. ## Why UUIDs UUIDs keep identity stable across renames, aliases, revisions, and localisations. `dialogue.character` always points at the same character, regardless of what cue string was actually rendered (`MARA`, `MARA (V.O.)`, `M.`) or what language the dialogue is in. # Conformance Source: https://docs.screenjson.com/specification/conformance Validation rules, versioning, and the extension contract. A ScreenJSON document **conforms** to version *N* of this specification if, and only if, it validates successfully against the JSON Schema published at: ``` https://screenjson.com/draft//schema ``` and at `https://screenjson.com/schema.json` for the canonical draft. ## Guarantees for every conforming document * Exactly one root `id`, `version`, `title`, `lang`, `charset`, `dir`, `authors`, and `document`. * Every referenced UUID — author, contributor, character, scene, element, note, revision, bookmark — exists exactly once in its canonical array. * Every text map key is a syntactically valid BCP 47 tag. * No object contains properties beyond those permitted by the schema; every object's `additionalProperties` is `false`. * Extension data appears only under `meta`. ## Versioning ScreenJSON uses semantic versioning, recorded in the document's root `version`. | Bump | When | Guarantee | | - | - | - | | Major | Backwards-incompatible change | Documents authored against the prior major may not validate. | | Minor | New optional fields, new enum values, clarifications | Prior documents continue to validate. | | Patch | Editorial fixes only | No schema changes. | ## Tool expectations * Validators **MUST** reject documents whose root `version` declares a schema version they do not implement. * Tools **MUST NOT** silently drop unknown-but-well-formed fields. * Tools **MAY** warn on documents whose `version` is older than their target draft but validation still succeeds. ## Reference validator `screenjson validate --strict` is the reference implementation. Any validator implementing the current JSON Schema draft produces equivalent results on canonical documents. ## Extension model * Every object has a `meta` string-to-string map. * Extension data **MUST** live in `meta`, keyed under vendor-prefixed names — for example `com.example.color-of-the-week`. * Keys match `^[A-Za-z0-9_.:-]{1,64}$`; values are 0–2048 characters. ## Changelog ### 2026-01 Initial public draft. `https://screenjson.com/draft/2026-01/schema`. Document, scene, element, analysis, and encryption blocks stabilised. # Document root Source: https://docs.screenjson.com/specification/document-root Required and optional properties at the top of a ScreenJSON document. ## Required | Field | Type | Description | | - | - | - | | `id` | [uuid](/specification/shared-types#uuid) | RFC 4122 document identifier. | | `version` | string | Semver. Pins the schema version the document was authored against. | | `title` | [name](/specification/shared-types#name) | Document title, language-keyed. | | `lang` | [lang](/specification/shared-types#lang) | Primary BCP 47 language tag. | | `charset` | string | IANA character set label. Default `utf-8`. | | `dir` | enum | `ltr` or `rtl`. | | `authors` | array\[[author](/specification/authors)] | Must have at least one. | | `document` | object | The screenplay container — see below. | ## Optional | Field | Type | Description | | - | - | - | | `generator` | object | Tool that produced the file: `{ name, version, meta? }`. | | `locale` | string | BCP 47 locale — e.g. `en-US`. Default `en-US`. | | `contributors` | array | See [Authors & contributors](/specification/authors). | | `characters` | array\[[character](/specification/characters)] | Character index. | | `colors` | array\[color] | Reusable named colours. | | `sources` | array | Source works this screenplay is based on: `{ id, type, title, meta? }`. | | `registrations` | array | WGA / guild / national registry records. | | `revisions` | array\[[revision](/specification/revisions-notes#revisions)] | Document-level revision history. | | `encrypt` | object \| null | Default [encryption](/specification/encryption) parameters. | | `license` | object \| null | `{ id, ref?, meta? }`. | | `taggable` | slug\[] | Master index of every tag slug used in the document. Helper; optional. | | `genre` | slug\[] | Genre tags. | | `themes` | slug\[] | Thematic tags. | | `logline` | [text](/specification/shared-types#text) | One-sentence summary. | | `analysis` | object | See [Analysis](/specification/analysis). | ## `document` container | Field | Type | Description | | - | - | - | | `cover` | object | Cover page metadata. **Required.** | | `layout` | object | Optional rendering rules. See [Layout](/specification/layout). | | `bookmarks` | array | Stable shortcuts into scenes. | | `scenes` | array\[[scene](/specification/scenes)] | One or more scenes. **Required.** | | `meta` | [meta](/specification/shared-types#meta) | Extension. | ### `cover` | Field | Type | Description | | - | - | - | | `title` | [name](/specification/shared-types#name) | **Required.** | | `authors` | uuid\[] | Author ids shown on cover. **Required.** | | `sources` | uuid\[] | Source ids shown on cover. | | `extra` | [text](/specification/shared-types#text) | Free text. | | `meta` | meta | Extension. | ### `bookmarks` Stable shortcuts into scenes: | Field | Type | | - | - | | `id` | uuid | | `scene` | uuid | | `element` | uuid | | `title` | [text](/specification/shared-types#text) | | `desc` | text | | `meta` | meta | All fields except `desc` and `meta` are required. # Elements Source: https://docs.screenjson.com/specification/elements Typed scene-body elements. Every scene body is an ordered array of typed elements. Each element extends a common base and narrows with its own `type` field. ## Common element base Every element carries these fields: | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | Element identifier. | | `authors` | uuid\[] | ✓ | At least one author. | | `scene` | uuid | — | Back-reference to the parent scene. | | `contributors` | uuid\[] | — | Contributors to this element. | | `access` | [roles](/specification/shared-types) | — | RBAC roles that may read this element. | | `notes` | [note](/specification/revisions-notes#notes)\[] | — | Anchored annotations. | | `charset` | string | — | Override root charset. | | `dir` | enum | — | Override direction (`ltr` / `rtl`). | | `class` | slug | — | Custom class for renderer styling. | | `dom` | string (≤100) | — | Optional DOM hint for renderers. | | `encrypt` | [encrypt](/specification/encryption) | — | Per-element encryption override. | | `locked` | boolean | `false` | Renderer should not allow edits. | | `omit` | boolean | `false` | Marked for scheduled deletion. | | `revisions` | [revision](/specification/revisions-notes#revisions)\[] | — | Element-scoped revision markers. | | `styles` | slug\[] unique | — | Style ids to apply. | | `meta` | meta | — | Extension. | ## `action` Narrative description. ```json theme={null} { "type": "action", "text": { "en": "MARA lifts the lid." } } ``` | Field | Required | | - | - | | `type` | ✓ — `"action"` | | `text` | ✓ — language map | ## `character` (cue) Character cue — the line above dialogue. ```json theme={null} { "type": "character", "character": "", "display": "MARA (CONT'D)" } ``` | Field | Required | Description | | - | - | - | | `type` | ✓ — `"character"` | | | `character` | ✓ | UUID from the character index. | | `display` | — | Override the rendered cue string (e.g. `MARA (V.O.)`). Length 1–120. | ## `parenthetical` Bracketed actor direction. ```json theme={null} { "type": "parenthetical", "text": { "en": "(whispering)" } } ``` | Field | Required | | - | - | | `type` | ✓ — `"parenthetical"` | | `text` | ✓ — language map | ## `dialogue` A spoken line. ```json theme={null} { "type": "dialogue", "character": "", "origin": "V.O.", "dual": false, "text": { "en": "We have ninety seconds." } } ``` | Field | Required | Description | | - | - | - | | `type` | ✓ — `"dialogue"` | | | `character` | ✓ | UUID from the character index. | | `text` | ✓ | Language map. | | `origin` | — | `V.O`, `V.O.`, `O.S`, `O.S.`, `O.C`, `O.C.`, `FILTER`. | | `dual` | — | Dual-dialogue marker. Default `false`. | ## `shot` Camera direction. ```json theme={null} { "type": "shot", "text": { "en": "CLOSE ON MARA'S HANDS" }, "fov": 40, "perspective": "2D" } ``` | Field | Required | Description | | - | - | - | | `type` | ✓ — `"shot"` | | | `text` | ✓ | Language map. | | `fov` | — | Field-of-view in degrees. 0–360. Default `40`. | | `perspective` | — | `2D` or `3D`. Default `2D`. | ## `transition` Scene transitions — `CUT TO:`, `FADE TO BLACK.`, `SMASH CUT TO:`. ```json theme={null} { "type": "transition", "text": { "en": "CUT TO:" } } ``` ## `general` A typed catch-all for scene-body text that does not fit the other element kinds. ```json theme={null} { "type": "general", "text": { "en": "— END OF ACT ONE —" } } ``` ## Element order The `body` array preserves source order. Renderers use that order; there is no implicit sort. # Encryption Source: https://docs.screenjson.com/specification/encryption AES-256-CTR encryption of text runs. Structure, UUIDs, and metadata stay visible. ScreenJSON supports confidentiality without sacrificing structural introspection: **only text runs are encrypted**. UUIDs, timestamps, scene counts, character lists, revision labels, and `meta` stay in plain text. ## `encrypt` object ```json theme={null} { "encrypt": { "cipher": "aes-256-ctr", "hash": "sha256", "encoding": "hex", "meta": {} } } ``` | Field | Type | Required | Default | Values | | - | - | - | - | - | | `cipher` | string | ✓ | `aes-256-ctr` | Any symmetric cipher the tooling supports. | | `hash` | string | ✓ | `sha256` | Hash used for key derivation. | | `encoding` | enum | ✓ | `hex` | `hex`, `base16`, `base32`, `base64`, `ascii85`. | | `meta` | meta | — | `{}` | Free-form extension. | ## Scope * **Document-level** `encrypt` (on the document root) applies to every element that does not set its own. * **Element-level** `encrypt` on an individual element overrides the root. ## What is encrypted * Every value in a `text` language map on every element (action, dialogue, parenthetical, shot, transition, general, cue). * `note.text`. * Slugline `desc`. * Character `desc`. * Logline. * Cover `extra`. * Any other `text` map inside the document tree that sits under a scope with encryption enabled. ## What is not * All UUIDs. * All timestamps (`created`, `modified`, `updated`). * Tag arrays — `props`, `sfx`, `locations`, `genre`, `themes`, every scene taxonomy, plus `taggable`. * Character canonical `name` and `aliases`. * `meta` maps. * Everything under `analysis` — embeddings, passages, summaries. ## Key derivation The encryption key is derived from the shared secret via a single pass of the declared `hash`. Default: SHA-256. Keys must be **at least 10 characters**. Shorter keys are rejected by `screenjson encrypt`. ## CLI entry points * [`screenjson encrypt`](/tools/cli/encrypt) * [`screenjson decrypt`](/tools/cli/decrypt) * [`screenjson convert --encrypt `](/tools/cli/convert) * [`screenjson export --decrypt `](/tools/cli/export) ## Environment variable `SCREENJSON_ENCRYPT_KEY` is read by every CLI command that takes a `--key` / `--encrypt` / `--decrypt` flag when no explicit value is provided. # Layout Source: https://docs.screenjson.com/specification/layout Optional rendering hints under document.layout. `document.layout` is an optional block that tells renderers how to present the document. Everything here is **informational** — viewers may honour, partially honour, or ignore it. ## Shape ```json theme={null} { "document": { "layout": { "header": { /* ribbon */ }, "footer": { /* ribbon */ }, "status": { /* status */ }, "styles": [ /* style */ ], "templates": [ /* template */ ], "guides": [ /* format */ ], "meta": {} } } } ``` ## `ribbon` Repeating header / footer content. ```json theme={null} { "cover": false, "show": true, "start": 2, "omit": [], "text": { "en": "DRAFT — CONFIDENTIAL" } } ``` | Field | Type | Required | Description | | - | - | - | - | | `cover` | boolean | ✓ | Show on the cover page. | | `show` | boolean | ✓ | Visibility toggle. | | `text` | [text](/specification/shared-types#text) | ✓ | Ribbon text. | | `start` | integer ≥0 | — | First page the ribbon appears on. Default `0`. | | `omit` | integer\[] unique | — | Pages to skip. | | `meta` | meta | — | Extension. | ## `status` Industry revision status. ```json theme={null} { "color": "blue", "round": 2, "updated": "2026-01-14T10:12:00Z" } ``` | Field | Type | Required | Values | | - | - | - | - | | `color` | enum | ✓ | `white`, `blue`, `pink`, `yellow`, `green`, `goldenrod`, `buff`, `salmon`, `cherry`. | | `round` | integer ≥1 | ✓ | Revision round. Default `1`. | | `updated` | datetime | ✓ | ISO 8601. | ## `style` A reusable presentation-style fragment. ```json theme={null} { "id": "big-quote", "default": false, "text": "font-size: 18pt; font-weight: 700;" } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | slug | ✓ | Referenced by `element.styles[]`. | | `default` | boolean | ✓ | Whether renderers should apply automatically. | | `text` | string (0–5000) | ✓ | Renderer-specific style payload. | | `meta` | meta | — | Extension. | ## `template` A longer reusable fragment, size-limited separately. | Field | Type | Required | | - | - | - | | `id` | slug | ✓ | | `default` | boolean | ✓ | | `text` | string (0–20,000) | ✓ | | `meta` | meta | — | ## `guides` References to external presentation standards. ```json theme={null} { "id": "wb-final-draft", "title": "Warner Bros / Final Draft convention", "url": "https://screenjson.com/tools/screenjson-ui/#formatting-standards", "note": "US Letter, Courier Prime 12pt, 60 lines/page." } ``` # Overview Source: https://docs.screenjson.com/specification/overview The ScreenJSON schema — structure, principles, and where to find the authoritative definition. ScreenJSON is a JSON Schema for screenplay documents. The authoritative machine-readable definition lives at [`https://screenjson.com/schema.json`](https://screenjson.com/schema.json). ## Top level ```json theme={null} { "id": "", "version": "1.0.0", "title": { "en": "The Heist" }, "lang": "en", "charset": "utf-8", "dir": "ltr", "authors": [ /* one or more */ ], "document": { "cover": { ... }, "scenes": [ ... ] } } ``` ### Required at the root `id`, `version`, `title`, `lang`, `charset`, `dir`, `authors`, `document`. ### Optional at the root `generator`, `locale`, `contributors`, `characters`, `colors`, `sources`, `registrations`, `revisions`, `encrypt`, `license`, `taggable`, `genre`, `themes`, `logline`, `analysis`. ## Schema draft Current draft id: `https://screenjson.com/draft/2026-01/schema`. Documents pin their authored schema via the root `version` field (semver). ## Design commitments * **Structural, not presentational.** Element types describe what a thing *is*, not how it looks. Presentation lives in the optional `layout` block. * **Language as first-class.** Every user-facing text field is a BCP 47 keyed map. * **Stable identity.** Every referenceable node carries an RFC 4122 UUID. * **`additionalProperties: false` everywhere.** Extension data lives only in `meta`. * **Analysis is additive.** Everything under `analysis` is optional and discardable. ## Reference pages Required and optional properties at the top of a document. uuid, slug, lang, text, name, meta, tags, roles, datetime. Credit model. Character index. Scene shape with cast and production tags. Structured scene headings. Typed scene body elements. Editorial passes and anchored annotations. Optional rendering hints. Embeddings, passages, summaries. AES-256-CTR content encryption. Validation, versioning, extensions. # Revisions & notes Source: https://docs.screenjson.com/specification/revisions-notes Editorial pass markers and anchored annotations. ## Revisions A revision describes a discrete editorial pass. Lives at the document root (`revisions[]`) or inside an element (`element.revisions[]`). ```json theme={null} { "id": "", "parent": "", "index": 3, "authors": [""], "label": "blue", "created": "2026-01-14T10:12:00Z" } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | Revision identifier. | | `index` | integer ≥0 | ✓ | Ordinal index within the parent revision chain. | | `authors` | uuid\[] | ✓ | At least one author id. | | `label` | slug (3–30) | ✓ | Named label. Common values: `draft`, `blue`, `pink`, `yellow`, `production-draft`, `final`. | | `created` | datetime | ✓ | ISO 8601. | | `parent` | uuid | — | Revision this was forked from. | | `meta` | meta | — | Extension. | ### Status block The industry revision status — lives under `document.layout.status`: ```json theme={null} { "color": "blue", "round": 2, "updated": "2026-01-14T10:12:00Z" } ``` `color` enum (industry-standard order): ``` white · blue · pink · yellow · green · goldenrod · buff · salmon · cherry ``` ## Notes Notes are annotations anchored to an element (or another note) via character index ranges. ```json theme={null} { "id": "", "parent": "", "highlight": [[12, 27]], "contributor":"", "created": "2026-01-14T10:20:00Z", "text": { "en": "Too on-the-nose." }, "color": "amber", "meta": {} } ``` | Field | Type | Required | Description | | - | - | - | - | | `id` | uuid | ✓ | Note identifier. | | `created` | datetime | ✓ | ISO 8601. | | `text` | [text](/specification/shared-types#text) | ✓ | Language map. | | `parent` | uuid | — | Element (or note) the note is attached to. | | `highlight` | `[[int, int], ...]` | — | Array of `[start, end]` Unicode codepoint ranges within the rendered text of the parent element. | | `contributor` | uuid | — | Contributor who wrote the note. | | `color` | slug | — | Reference to a `colors[].id`. | | `meta` | meta | — | Extension. | ## Why UUIDs for notes Anchored via `parent: uuid` and `highlight` ranges, notes survive rewrites of the surrounding element as long as the targeted stretch of text is still present. Renderers look up `parent`, find the element, and apply the highlight ranges to the rendered text. # Scenes Source: https://docs.screenjson.com/specification/scenes Scene shape — heading, body, cast, production-breakdown taxonomies. ## Shape ```json theme={null} { "id": "", "authors": [""], "heading": { /* slugline */ }, "body": [ /* elements */ ], "cast": [""], "animals": [], "extra": [], "locations": [], "moods": [], "props": [], "sfx": [], "sounds": [], "tags": [], "vfx": [], "wardrobe": [], "contributors": [], "meta": {} } ``` ## Required | Field | Type | Description | | - | - | - | | `id` | uuid | Scene identifier. | | `authors` | uuid\[] (≥1) | Author ids from the root. | | `heading` | [slugline](/specification/sluglines) | Structured scene heading. | | `body` | array | Ordered list of [elements](/specification/elements). May be empty for outlining. | ## Optional | Field | Type | Description | | - | - | - | | `contributors` | uuid\[] | Contributors who touched this scene. | | `cast` | uuid\[] unique | Characters present in the scene. Projection over `body`. | | `animals` | slug\[] unique | Any animal taxonomy. | | `extra` | slug\[] unique | Extras required. | | `locations` | slug\[] unique | Additional location tags beyond the heading. | | `moods` | slug\[] unique | Mood tags. | | `props` | slug\[] unique | Props. | | `sfx` | slug\[] unique | Special effects. | | `sounds` | slug\[] unique | Sound design cues. | | `tags` | slug\[] unique | Free-form editorial tags. | | `vfx` | slug\[] unique | Visual effects. | | `wardrobe` | slug\[] unique | Wardrobe. | | `meta` | meta | Extension. | ## Breakdown semantics The ten production taxonomies — `animals`, `extra`, `locations`, `moods`, `props`, `sfx`, `sounds`, `tags`, `vfx`, `wardrobe` — are each a unique array of slugs. Populated by breakdown tools, they make scheduling, budgeting, and discovery queryable without re-parsing scene text. A prop referenced as `"revolver"` in scene 3 is the same prop as `"revolver"` in scene 47 — the slug is the identifier. ## `cast` as a projection `cast` is redundant with the character references inside `body` elements, but is kept as an explicit array so tooling can answer "who is in this scene" without walking the body. It should contain every character uuid referenced by any `dialogue` or `character` (cue) element inside `body`. # Shared types Source: https://docs.screenjson.com/specification/shared-types Scalar and small-object types reused across the schema. ScreenJSON reuses a small set of types everywhere. Understanding them once makes the rest of the schema read itself. ## `uuid` RFC 4122 UUID string. Pattern `8-4-4-4-12`, hex. ## `slug` Lowercase, kebab-cased identifier. \| Pattern | `^[a-z0-9]+(?:-[a-z0-9]+)*$` | \| Length | 3–50 characters | \| Example | `production-draft`, `revolver`, `mara` | ## `lang` BCP 47 language tag. \| Pattern | `^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$` | \| Default | `en` | \| Examples | `en`, `en-GB`, `fr-CA`, `ja`, `zh-Hant`, `sr-Latn-RS` | ## `dir` \| Values | `ltr`, `rtl` | \| Default | `ltr` | ## `charset` IANA character set label. \| Pattern | `^[A-Za-z0-9][A-Za-z0-9._:-]{1,31}$` | \| Default | `utf-8` | ## `datetime` ISO 8601 date-time. Format `date-time`. ## `text` A map keyed by `lang` with text values. \| Pattern for keys | `lang` regex | \| Value length | 1–10,000 characters | \| Default | `{}` | \| Used on | action, dialogue, parenthetical, shot, transition, general `text`; note `text`; slugline `desc`; character `desc`; source `title`; logline; cover `extra`; ribbon `text`; passage `text`; summary `text`. | ## `name` Short-form variant of `text`. \| Value length | 1–255 characters | \| Used on | root `title`, character `name` (wait: `name` is a single string, see below), cover `title`, source `title` when short. | ## `meta` Free-form string key/value extension map. \| Key pattern | `^[A-Za-z0-9_.:-]{1,64}$` | \| Value length | 0–2,048 characters | \| Default | `{}` | \| Present on | every object. The **only** place extension data is permitted. | ## `tags` and `roles` Arrays of unique [slug](#slug) values. Order is not semantic. ## `format` Reference to an external screenplay presentation standard, informational only. ```json theme={null} { "id": "wb-final-draft", "title": "Warner Bros / Final Draft convention", "url": "https://...", "note": "..." } ``` ## `color` Reusable named colour. ```json theme={null} { "id": "red", "rgb": [220, 38, 38], "hex": "#DC2626" } ``` ## `registration` ```json theme={null} { "authority": "WGA-West", "id": "R-1234567", "created": "2026-01-02T10:00:00Z", "modified": "2026-01-10T12:30:00Z" } ``` ## `license` ```json theme={null} { "id": "CC-BY-NC-4.0", "ref": "https://creativecommons.org/licenses/by-nc/4.0/" } ``` # Sluglines Source: https://docs.screenjson.com/specification/sluglines The structured scene heading. The slugline — the **scene heading** — is one of the few places in screenwriting where formatting is the structure. ScreenJSON decomposes it into parts. ## Shape ```json theme={null} { "no": 14, "context": "INT.", "setting": "VAULT — SUB-LEVEL 3", "time": "NIGHT", "mods": ["FLASHBACK", "ESTABLISHING"], "desc": { "en": "Low-ceilinged. Emergency lighting." }, "meta": {} } ``` ## Fields | Field | Type | Required | Description | | - | - | - | - | | `context` | enum | ✓ | `I/E`, `INT/EXT`, `EXT/INT`, `INT`, `EXT`, `POV`. | | `setting` | string (1–200) | ✓ | Location text, free-form. | | `time` | string | ✓ | Time-of-day / continuity label. See below. | | `no` | integer ≥1 | — | Production scene number. | | `mods` | string\[] unique, each 1–40 chars | — | Additional slugline modifiers — `INTERCUT`, `FLASHBACK`, `ESTABLISHING`, `SAME`, etc. | | `desc` | [text](/specification/shared-types#text) | — | Language-keyed description. | | `meta` | meta | — | Extension. | ## `time` values **Enumerated** (preferred): ``` DAY · NIGHT · DAWN · DUSK · LATER · MOMENTS LATER · CONTINUOUS · MORNING · AFTERNOON · EVENING · THE NEXT DAY ``` **Custom** values are allowed, constrained by the pattern: ``` ^[A-Z0-9][A-Z0-9 .'’/-]{1,39}$ ``` Uppercase, 2–40 chars, alphanumeric plus space, period, apostrophe, slash, hyphen. ## Rendering Renderers typically reassemble the slugline into the traditional `INT. SETTING - TIME` form: ``` INT. VAULT — SUB-LEVEL 3 - NIGHT ``` The `mods` array is rendered inline with a dash separator: ``` INT. VAULT - NIGHT - FLASHBACK ``` Language rendering uses the per-scene or per-element `lang` override if present, otherwise the document root `lang`. # convert Source: https://docs.screenjson.com/tools/cli/convert Convert screenplay formats to ScreenJSON. ``` screenjson convert -i [-o ] [options] ``` Converts **FDX**, **FadeIn**, **Fountain**, **PDF**, **JSON**, or **YAML** input into a ScreenJSON document. ## Flags ### Core | Flag | Default | Description | | - | - | - | | `-i` · `--input ` | — | Input file. **Required.** | | `-o` · `--output ` | stdout | Output file (or stdout if omitted / `-`). | | `-f` · `--format ` | auto | Force input format: `fdx` · `fadein` · `fountain` · `celtx` · `pdf`. | | `--yaml` | `false` | Emit YAML instead of JSON. | | `--lang ` | `en` | Primary language tag (BCP 47). | | `--encrypt ` | — | Encrypt content runs with a shared secret (min 10 chars). | | `-q` · `--quiet` | `false` | Suppress non-error output. | | `--verbose` | `false` | Verbose logging to stderr. | ### PDF import | Flag | Default | Description | | - | - | - | | `--pdftohtml ` | `$SCREENJSON_PDFTOHTML` or `/opt/homebrew/bin/pdftohtml` | Path to Poppler's `pdftohtml`. | | `--pdf-password ` | — | Password for encrypted source PDFs. | ### Database output | Flag | Description | | - | - | | `--db ` | `elasticsearch` · `mongodb` · `cassandra` · `dynamodb` · `redis`. | | `--db-host ` | Database host. | | `--db-port ` | Database port. | | `--db-user ` | Database username. | | `--db-pass ` | Database password. | | `--db-collection ` | Collection / index name. | ### Blob output | Flag | Description | | - | - | | `--blob ` | `s3` · `azure` · `minio`. | | `--blob-bucket ` | Bucket name. | | `--blob-key ` | Object key / path. Default `.json`. | | `--blob-region ` | AWS region (for S3). | | `--blob-endpoint ` | Custom endpoint (for MinIO). | ## Format auto-detection The converter auto-detects input format from file extension: | Extension | Format | | - | - | | `.fdx` | `fdx` | | `.fadein` | `fadein` | | `.fountain`, `.spmd` | `fountain` | | `.pdf` | `pdf` | | `.json` | `json` | | `.yaml`, `.yml` | `yaml` | Files without a recognised extension require `-f`. ## Examples ```bash theme={null} # FDX → ScreenJSON screenjson convert -i screenplay.fdx -o screenplay.json # Fountain → YAML screenjson convert -i screenplay.fountain --yaml -o screenplay.yaml # PDF with custom pdftohtml screenjson convert -i script.pdf --pdftohtml /opt/homebrew/bin/pdftohtml -o script.json # Password-protected PDF screenjson convert -i protected.pdf --pdf-password "secret" -o script.json # Convert and encrypt in one shot screenjson convert -i script.fdx --encrypt "AtLeast10Chars!" -o cipher.json # Stdout → jq screenjson convert -i script.fdx | jq '.title.en' # Store directly to MongoDB screenjson convert -i script.fdx \ --db mongodb \ --db-host mongodb://localhost:27017 \ --db-collection screenplays ``` ## Notes * The converter auto-validates the output and prints a warning to stderr on validation errors. Use [`validate`](/tools/cli/validate) for strict exit codes. * Database and blob output are additive — the converter writes the file, then the DB, then the blob, in order. # decrypt Source: https://docs.screenjson.com/tools/cli/decrypt Decrypt an encrypted ScreenJSON document. ``` screenjson decrypt -i -o --key ``` Reverses a previous `screenjson encrypt` pass. ## Flags | Flag | Default | Description | | - | - | - | | `-i` · `--input ` | — | Encrypted ScreenJSON file. **Required.** | | `-o` · `--output ` | — | Decrypted output path. **Required.** | | `--key ` | `$SCREENJSON_ENCRYPT_KEY` | Shared secret used to encrypt the document. | ## Examples ```bash theme={null} # Explicit key screenjson decrypt -i cipher.json -o plain.json --key "AtLeast10Chars!" # Key from environment export SCREENJSON_ENCRYPT_KEY="AtLeast10Chars!" screenjson decrypt -i cipher.json -o plain.json # Decrypt on the way out to a screenplay format screenjson export -i cipher.json -f fdx --decrypt "AtLeast10Chars!" -o script.fdx ``` ## Errors A wrong key fails with a clear error rather than silently producing junk output. The operation is strict: partial decrypts are not emitted. | Error | Meaning | | - | - | | `Error: decryption key required` | No `--key` and `SCREENJSON_ENCRYPT_KEY` unset. | | `Error decrypting: ...` | Key mismatch or document not encrypted. | # Docker Source: https://docs.screenjson.com/tools/cli/docker Run screenjson-cli as a Docker container in CLI mode or server mode. ## Pull ```bash theme={null} docker pull screenjson/cli:latest ``` The image ships Poppler's `pdftohtml` for PDF import; no extra setup required. ## CLI mode Mount the current directory and run any subcommand: ```bash theme={null} # Help docker run --rm screenjson/cli help # Convert a local file docker run --rm -v "$PWD:/data" screenjson/cli \ convert -i /data/screenplay.fdx -o /data/screenplay.json # Validate docker run --rm -v "$PWD:/data" screenjson/cli \ validate -i /data/screenplay.json --strict # Export to PDF (A4) docker run --rm -v "$PWD:/data" screenjson/cli \ export -i /data/screenplay.json -f pdf --pdf-paper a4 -o /data/screenplay.pdf ``` ## Server mode ```bash theme={null} docker run --rm -p 8080:8080 \ -e SCREENJSON_GOTENBERG_URL=http://gotenberg:3000 \ screenjson/cli serve --port 8080 ``` ## docker-compose ```yaml theme={null} services: screenjson: image: screenjson/cli:latest ports: ["8080:8080"] environment: - SCREENJSON_GOTENBERG_URL=http://gotenberg:3000 - SCREENJSON_TIKA_URL=http://tika:9998 command: ["serve", "--port", "8080"] gotenberg: image: gotenberg/gotenberg:8 ports: ["3000:3000"] tika: image: apache/tika:latest ports: ["9998:9998"] ``` ## Kubernetes The same image runs as a `Deployment` in server mode. See [Greenlight deploy](/tools/greenlight/deploy) for an opinionated, queue-backed production configuration on top of this image. # encrypt Source: https://docs.screenjson.com/tools/cli/encrypt Encrypt content runs in a ScreenJSON document with AES-256-CTR. ``` screenjson encrypt -i -o --key ``` Encrypts every text run in a ScreenJSON document using AES-256-CTR with SHA-256 key derivation. Structure, UUIDs, timestamps, and metadata remain visible. ## Flags | Flag | Default | Description | | - | - | - | | `-i` · `--input ` | — | Input ScreenJSON file. **Required.** | | `-o` · `--output ` | — | Output path. **Required.** | | `--key ` | `$SCREENJSON_ENCRYPT_KEY` | Shared secret — at least 10 characters. | ## Cipher details * **Cipher:** AES-256-CTR * **Key derivation:** SHA-256 of the shared secret * **Binary encoding:** `hex` (default), overridable via `document.encrypt.encoding` on the output. Valid values: `hex`, `base16`, `base32`, `base64`, `ascii85`. ## What is encrypted Every value in a `text` language map on every element; plus notes, slugline descriptions, character descriptions, loglines, cover extras. See [Encryption](/concepts/encryption). ## What is not UUIDs, timestamps, tag arrays, character canonical names, `meta`, and everything under `analysis` — so indexers, schedulers, and breakdown tools can still read structure without decrypting content. ## Examples ```bash theme={null} # Explicit key screenjson encrypt -i plain.json -o cipher.json --key "AtLeast10Chars!" # Key from environment export SCREENJSON_ENCRYPT_KEY="AtLeast10Chars!" screenjson encrypt -i plain.json -o cipher.json # Encrypt at convert time (one-shot) screenjson convert -i script.fdx --encrypt "AtLeast10Chars!" -o cipher.json ``` ## Errors | Error | Meaning | | - | - | | `Error: encryption key required` | No `--key` provided and `SCREENJSON_ENCRYPT_KEY` is unset. | | `Error parsing JSON` | Input is not valid ScreenJSON. | | `Error encrypting: key too short` | Shared secret is less than 10 characters. | # Environment variables Source: https://docs.screenjson.com/tools/cli/environment Every environment variable screenjson-cli reads. Every CLI flag has a corresponding environment variable. Flags take precedence when both are set. ## General | Variable | Default | Description | | - | - | - | | `SCREENJSON_ENCRYPT_KEY` | — | Default encryption / decryption key. Used by `encrypt`, `decrypt`, `convert --encrypt`, `export --decrypt`. | | `SCREENJSON_PDFTOHTML` | `/opt/homebrew/bin/pdftohtml` | Path to Poppler's `pdftohtml` binary. | | `SCREENJSON_PDF_PAPER` | `letter` | Default paper size for PDF export: `letter` · `a4`. | | `SCREENJSON_PDF_FONT` | `courier` | Default font for PDF export: `courier` · `courier-prime`. | ## Server Used by `screenjson serve`. | Variable | Default | Description | | - | - | - | | `SCREENJSON_SERVER_HOST` | `0.0.0.0` | Listen host. | | `SCREENJSON_SERVER_PORT` | `8080` | Listen port. | | `SCREENJSON_SERVER_WORKERS` | CPU cores | Worker pool size. | ## External services | Variable | Default | Description | | - | - | - | | `SCREENJSON_GOTENBERG_URL` | — | Gotenberg URL for HTML→PDF rendering. | | `SCREENJSON_TIKA_URL` | — | Apache Tika URL for content extraction. | | `SCREENJSON_LLM_URL` | — | OpenAI-compatible LLM endpoint. | | `SCREENJSON_LLM_APIKEY` | — | LLM API key. | | `SCREENJSON_LLM_MODEL` | — | Model identifier. | ## Database Used by `convert --db ...`. | Variable | Description | | - | - | | `SCREENJSON_DB_TYPE` | `elasticsearch` · `mongodb` · `cassandra` · `dynamodb` · `redis`. | | `SCREENJSON_DB_HOST` | Host. | | `SCREENJSON_DB_PORT` | Port. | | `SCREENJSON_DB_USER` | Username. | | `SCREENJSON_DB_PASS` | Password. | | `SCREENJSON_DB_COLLECTION` | Collection or index name. | | `SCREENJSON_DB_AUTH_TYPE` | `basic` · `apikey` · `token`. | | `SCREENJSON_DB_APIKEY` | API key (when `auth_type = apikey`). | | `SCREENJSON_DB_INDEX` | Elasticsearch index name. | | `SCREENJSON_DB_REGION` | DynamoDB region. | ## Blob storage Used by `convert --blob ...`. | Variable | Description | | - | - | | `SCREENJSON_BLOB_TYPE` | `s3` · `azure` · `minio`. | | `SCREENJSON_BLOB_BUCKET` | Bucket name. | | `SCREENJSON_BLOB_KEY` | Object key / path. | | `SCREENJSON_BLOB_REGION` | AWS region. | | `SCREENJSON_BLOB_ENDPOINT` | Custom endpoint (MinIO). | | `SCREENJSON_AWS_ACCESS_KEY` | AWS access key ID. | | `SCREENJSON_AWS_SECRET_KEY` | AWS secret access key. | # export Source: https://docs.screenjson.com/tools/cli/export Export a ScreenJSON document to a screenplay format. ``` screenjson export -i -f [-o ] [options] ``` Exports a ScreenJSON document to **FDX**, **FadeIn**, **Fountain**, **PDF**, **JSON**, or **YAML**. ## Flags ### Core | Flag | Default | Description | | - | - | - | | `-i` · `--input ` | — | ScreenJSON input file. **Required.** | | `-o` · `--output ` | stdout | Output file (or stdout if omitted / `-`). | | `-f` · `--format ` | — | **Required.** Output format: `fdx` · `fadein` · `fountain` · `pdf` · `json` · `yaml`. | | `--decrypt ` | — | Decrypt encrypted content runs before exporting. | | `--lang ` | `en` | Primary language to pick from each text map. | | `-q` · `--quiet` | `false` | Suppress non-error output. | | `--verbose` | `false` | Verbose logging to stderr. | ### PDF export | Flag | Default | Description | | - | - | - | | `--pdf-paper ` | `letter` | Paper size: `letter` · `a4`. | | `--pdf-font ` | `courier` | Font: `courier` · `courier-prime`. | | `--gotenberg ` | — | [Gotenberg](https://gotenberg.dev/) URL for HTML→PDF rendering. Optional; falls back to the native PDF encoder. | ## Examples ```bash theme={null} # FDX export screenjson export -i script.json -f fdx -o script.fdx # Fountain screenjson export -i script.json -f fountain -o script.fountain # PDF with A4 paper screenjson export -i script.json -f pdf --pdf-paper a4 -o script.pdf # Decrypt and export screenjson export -i cipher.json -f fdx --decrypt "SharedSecret123" -o script.fdx # Pick French as the primary language screenjson export -i multilang.json -f fdx --lang fr -o script-fr.fdx # External Gotenberg renderer screenjson export -i script.json -f pdf --gotenberg http://gotenberg:3000 -o script.pdf ``` ## Behaviour * Output is written to stdout if `-o` is omitted or set to `-`. * The binary accepts ScreenJSON in either JSON or YAML; it detects by attempting JSON parse first. * `--lang` controls which key is pulled from each text map at export time. Missing keys fall through to the document's root `lang`. # formats Source: https://docs.screenjson.com/tools/cli/formats List every format the running binary understands. ``` screenjson formats ``` Prints every format registered in the pipeline, with its file extensions and whether it can be used for import, export, or both. ## Output shape ``` Supported Formats: ---------------------------------------------------------------------- fdx Final Draft FDX format Extensions: .fdx Capabilities: import, export fadein FadeIn OSF format (ZIP with document.xml) Extensions: .fadein Capabilities: import, export fountain Fountain plain-text screenplay format Extensions: .fountain, .spmd Capabilities: import, export pdf PDF screenplay format Extensions: .pdf Capabilities: import, export json ScreenJSON native format Extensions: .json Capabilities: import, export yaml YAML serialization of ScreenJSON Extensions: .yaml, .yml Capabilities: import, export celtx Celtx project format (placeholder) Extensions: .celtx Capabilities: (not yet implemented) ``` ## Use in scripts The `formats` output is intended to be human-readable. For programmatic use, hit [`GET /formats`](/api-reference/cli/endpoint/list-formats) on a running server instead — it returns the same data as JSON with additional MIME type information. # Install Source: https://docs.screenjson.com/tools/cli/install Install screenjson-cli via Docker, from source, or as a pre-built binary. `screenjson-cli` is commercial. The [source repository](https://github.com/screenjson/screenjson-cli) is private. Contact `hello@screenjson.com` to request access. For the free OSS subset (FDX / Fountain / FadeIn → ScreenJSON), use [`screenjson-export`](/tools/export/install). ## Docker (recommended) ```bash theme={null} docker pull screenjson/cli:latest docker run --rm screenjson/cli help ``` The image includes Poppler's `pdftohtml` binary for PDF import. ## From source (requires repo access) ```bash theme={null} git clone https://github.com/screenjson/screenjson-cli.git cd screenjson-cli go build -trimpath -o screenjson ./cmd/screenjson sudo mv screenjson /usr/local/bin/ ``` Requires Go 1.22+. ### PDF import prerequisites PDF import additionally requires [Poppler](https://poppler.freedesktop.org/)'s `pdftohtml`: ```bash theme={null} # macOS brew install poppler # Debian / Ubuntu sudo apt-get install poppler-utils ``` Typical binary paths: | Platform | Path | | - | - | | macOS · Apple Silicon | `/opt/homebrew/bin/pdftohtml` | | macOS · Intel | `/usr/local/bin/pdftohtml` | | Linux | `/usr/bin/pdftohtml` | Override with `--pdftohtml ` or `SCREENJSON_PDFTOHTML`. ## Verify ```bash theme={null} screenjson version # → screenjson version 1.0.0 ``` # Overview Source: https://docs.screenjson.com/tools/cli/overview screenjson-cli is the full, commercial ScreenJSON toolchain — every format, validation, encryption, REST API. `screenjson-cli` is the full, production-grade ScreenJSON toolchain. One Go binary exposes nine subcommands. ## Commands | Command | Purpose | | - | - | | [`convert`](/tools/cli/convert) | Read **FDX**, **Fountain**, **FadeIn**, or **PDF** → emit ScreenJSON (or YAML). | | [`export`](/tools/cli/export) | Read ScreenJSON → emit **FDX**, **Fountain**, **FadeIn**, **PDF**, JSON, or YAML. | | [`validate`](/tools/cli/validate) | Check against the ScreenJSON JSON Schema. | | [`encrypt`](/tools/cli/encrypt) | AES-256-CTR encrypt every text run. | | [`decrypt`](/tools/cli/decrypt) | Decrypt an encrypted ScreenJSON document. | | [`serve`](/tools/cli/serve) | Start the REST API server. | | [`formats`](/tools/cli/formats) | List supported formats and their capabilities. | | `version` | Print version. | | `help` | Print top-level help. | ## Supported formats | Format | Extensions | Import | Export | | - | - | - | - | | Final Draft | `.fdx` | ✅ | ✅ | | FadeIn | `.fadein` | ✅ | ✅ | | Fountain | `.fountain`, `.spmd` | ✅ | ✅ | | PDF | `.pdf` | ✅ (via Poppler) | ✅ | | ScreenJSON | `.json` | ✅ | ✅ | | YAML | `.yaml`, `.yml` | ✅ | ✅ | | Celtx | `.celtx` | — | — (placeholder) | ## Global patterns Every command accepts long and short forms for input and output paths: * `-i` · `--input ` * `-o` · `--output ` * `-f` · `--format ` (where applicable) * `-q` · `--quiet` * `--verbose` ## REST API `screenjson serve` exposes the same engine over HTTP. See [API Reference → screenjson-cli server](/api-reference/cli/introduction). ## License Commercial — contact `hello@screenjson.com`. For the free OSS subset (writer-format import only), use [`screenjson-export`](/tools/export/overview). # serve Source: https://docs.screenjson.com/tools/cli/serve Start the REST API server. ``` screenjson serve [options] ``` Starts a long-running HTTP server exposing the conversion, export, and validation engine. ## Flags | Flag | Default | Description | | - | - | - | | `--host ` | `0.0.0.0` | Listen host. | | `--port ` | `8080` | Listen port. | | `--workers ` | CPU cores | Worker pool size. | | `--gotenberg ` | — | Gotenberg URL for HTML→PDF rendering. | | `--tika ` | — | Apache Tika URL for content extraction. | | `--llm ` | — | OpenAI-compatible LLM endpoint. | External URLs also come from environment variables (`SCREENJSON_GOTENBERG_URL`, `SCREENJSON_TIKA_URL`, `SCREENJSON_LLM_URL`). ## Endpoints | Method | Path | Purpose | | - | - | - | | `GET` | `/` | Queue status and metrics. | | `GET` | `/health` | Health check, including per-service status. | | `GET` | `/formats` | List supported formats. | | `POST` | `/convert` | Upload a file, receive ScreenJSON. | | `POST` | `/export` | Upload ScreenJSON, receive a format. | | `POST` | `/validate` | Validate a ScreenJSON document. | Full request/response details: [API Reference → screenjson-cli endpoints](/api-reference/cli/introduction). ## Request limits * `/convert`, `/export`: **50 MB** request body. * `/validate`: **10 MB** request body. ## CORS The server emits a permissive CORS policy: * `Access-Control-Allow-Origin: *` * `Access-Control-Allow-Methods: GET, POST, OPTIONS` * `Access-Control-Allow-Headers: Content-Type, Authorization` `OPTIONS` preflight returns `200` with the headers above. ## Examples ```bash theme={null} # Default screenjson serve # Custom port and pool screenjson serve --host 127.0.0.1 --port 3000 --workers 16 # With external services screenjson serve --gotenberg http://gotenberg:3000 --tika http://tika:9998 # Docker docker run --rm -p 8080:8080 screenjson/cli serve --port 8080 ``` ## Graceful shutdown The server catches `SIGINT` and `SIGTERM` and drains in-flight requests with a 30-second timeout before exiting. # Storage integrations Source: https://docs.screenjson.com/tools/cli/storage Database and blob storage drivers available on convert. `screenjson convert` can write its output to a database or object store in the same invocation that produces the ScreenJSON document. ## Databases ### Selectable via `--db` | Type | Notes | | - | - | | `mongodb` | Document store. Uses the ScreenJSON `id` as `_id`. | | `elasticsearch` | Indexed per `--db-collection`. | | `cassandra` | Table per `--db-collection`. | | `dynamodb` | Region from `--blob-region` or `SCREENJSON_BLOB_REGION`. Item-size limit applies. | | `redis` | Key-value store. Document stored as a serialised string. | ### Shared flags | Flag | Environment | Description | | - | - | - | | `--db ` | `SCREENJSON_DB_TYPE` | Backend selector. | | `--db-host ` | `SCREENJSON_DB_HOST` | Host / connection string. | | `--db-port ` | `SCREENJSON_DB_PORT` | Port. | | `--db-user ` | `SCREENJSON_DB_USER` | Username. | | `--db-pass ` | `SCREENJSON_DB_PASS` | Password. | | `--db-collection ` | `SCREENJSON_DB_COLLECTION` | Collection / index / table. | | — | `SCREENJSON_DB_AUTH_TYPE` | `basic` · `apikey` · `token`. | | — | `SCREENJSON_DB_APIKEY` | API key. | | — | `SCREENJSON_DB_INDEX` | Elasticsearch index name. | | — | `SCREENJSON_DB_REGION` | DynamoDB region. | ### Example ```bash theme={null} screenjson convert -i script.fdx \ --db mongodb \ --db-host mongodb://localhost:27017 \ --db-collection screenplays ``` ## Blob storage ### Selectable via `--blob` | Type | Notes | | - | - | | `s3` | AWS Simple Storage Service. | | `azure` | Azure Blob Storage. | | `minio` | Self-hosted, S3-compatible. Requires `--blob-endpoint`. | ### Shared flags | Flag | Environment | Description | | - | - | - | | `--blob ` | `SCREENJSON_BLOB_TYPE` | Backend selector. | | `--blob-bucket ` | `SCREENJSON_BLOB_BUCKET` | Bucket name. | | `--blob-key ` | `SCREENJSON_BLOB_KEY` | Object key. Defaults to `.`. | | `--blob-region ` | `SCREENJSON_BLOB_REGION` | AWS region. | | `--blob-endpoint ` | `SCREENJSON_BLOB_ENDPOINT` | Custom endpoint (MinIO, localstack). | | — | `SCREENJSON_AWS_ACCESS_KEY` | AWS access key ID. | | — | `SCREENJSON_AWS_SECRET_KEY` | AWS secret access key. | ### Examples ```bash theme={null} # S3 bucket screenjson convert -i script.fdx \ --blob s3 \ --blob-bucket my-bucket \ --blob-region us-west-2 # MinIO screenjson convert -i script.fdx \ --blob minio \ --blob-bucket screenplays \ --blob-endpoint http://minio:9000 # Azure screenjson convert -i script.fdx \ --blob azure \ --blob-bucket screenplays ``` ## Combining outputs `convert` can emit to a file, a database, **and** a blob in the same invocation. All three run after conversion succeeds: ```bash theme={null} screenjson convert -i script.fdx -o script.json \ --db mongodb --db-host mongodb://... --db-collection screenplays \ --blob s3 --blob-bucket archive --blob-region us-west-2 ``` # validate Source: https://docs.screenjson.com/tools/cli/validate Validate a ScreenJSON document against the schema. ``` screenjson validate -i [options] ``` Validates a ScreenJSON document against the [ScreenJSON JSON Schema](https://screenjson.com/schema.json). Prints a report and returns an exit code. ## Flags | Flag | Default | Description | | - | - | - | | `-i` · `--input ` | — | ScreenJSON file to validate. **Required.** | | `--strict` | `false` | Exit with code `1` when the document has validation errors. | | `--verbose` | `false` | Print the schema version used. | ## Exit codes | Code | Meaning | | - | - | | `0` | Document is valid, **or** invalid but `--strict` was not set. | | `1` | Document is invalid **and** `--strict` was set, or a runtime error occurred. | ## Examples ```bash theme={null} # Basic check, always returns 0 screenjson validate -i screenplay.json # CI-friendly: fail on invalid documents screenjson validate -i screenplay.json --strict # Verbose screenjson validate -i screenplay.json --verbose # Batch over a folder (bash) for f in screenplays/*.json; do screenjson validate -i "$f" --strict || echo "INVALID: $f" done ``` ## Output format Success: ``` ✓ Document is valid ``` Failure (exit `1` under `--strict`): ``` ✗ Document is invalid - /document/scenes/0/heading: missing required property: setting - /authors: array must have at least 1 item ``` ## Validator used The validator uses the schema version bundled with the binary. The current version is reported with `--verbose`. The same schema is available at [`https://screenjson.com/schema.json`](https://screenjson.com/schema.json) for use with any external validator (Ajv, jsonschema, gojsonschema). # Install Source: https://docs.screenjson.com/tools/export/install Install screenjson-export from a release binary, Docker, or source. ## Release binary Download the archive for your platform from [v1.0.0](https://github.com/screenjson/screenjson-export/releases/tag/v1.0.0), extract, and move to your PATH. ```bash theme={null} curl -sL \ https://github.com/screenjson/screenjson-export/releases/download/v1.0.0/screenjson-export_$(uname -s)_$(uname -m).tar.gz \ | tar -xz sudo mv screenjson-export /usr/local/bin/ ``` Archive names match the six supported targets: | Platform | Archive | | - | - | | macOS · Apple Silicon | `screenjson-export_Darwin_arm64.tar.gz` | | macOS · Intel | `screenjson-export_Darwin_x86_64.tar.gz` | | Linux · amd64 | `screenjson-export_Linux_x86_64.tar.gz` | | Linux · arm64 | `screenjson-export_Linux_arm64.tar.gz` | | Windows · amd64 | `screenjson-export_Windows_x86_64.zip` | | Windows · arm64 | `screenjson-export_Windows_arm64.zip` | Verify with SHA-256: ```bash theme={null} shasum -a 256 -c checksums.txt ``` ## Docker ```bash theme={null} docker pull screenjson/export:latest docker run --rm -v "$PWD:/data" screenjson/export -i /data/screenplay.fdx ``` The image is [distroless/static](https://github.com/GoogleContainerTools/distroless/tree/main/base), nonroot, \~5 MB. ## Go install ```bash theme={null} go install screenjson/export/cmd/screenjson-export@latest ``` ## From source ```bash theme={null} git clone https://github.com/screenjson/screenjson-export.git cd screenjson-export go build -trimpath -ldflags="-s -w" -o screenjson-export ./cmd/screenjson-export sudo mv screenjson-export /usr/local/bin/ ``` Requires Go 1.22+. ## Verify ```bash theme={null} screenjson-export --version # → screenjson-export v1.0.0 ``` # Overview Source: https://docs.screenjson.com/tools/export/overview screenjson-export is the free, open-source reference CLI. FDX / Fountain / FadeIn → ScreenJSON. `screenjson-export` is the minimal, open-source subset of the ScreenJSON CLI. It reads three writer formats and writes ScreenJSON. * **Final Draft** — `.fdx` * **Fountain** — `.fountain`, `.spmd` * **FadeIn** — `.fadein` MIT-licensed, single static binary, no CGO. [Source on GitHub](https://github.com/screenjson/screenjson-export). ## What it does * Reads FDX, Fountain, or FadeIn from disk. * Emits valid ScreenJSON on stdout or to a file. * Pretty-prints JSON by default. * Sets the primary language tag via `--lang`. ## What it does **not** do * No PDF import. * No export to FDX / Fountain / FadeIn / PDF. * No schema validation. * No encryption. * No server mode, no storage drivers, no MCP. For any of the above, use [`screenjson-cli`](/tools/cli/overview). ## Latest release v1.0.0 — [release page](https://github.com/screenjson/screenjson-export/releases/tag/v1.0.0). Six platform binaries + Docker image + `checksums.txt`. ## Source-level summary | Dependency | Why | | - | - | | `github.com/google/uuid` | RFC 4122 UUID generation for characters, scenes, elements. | No other external Go module. Format decoders live in `internal/formats/{fdx,fountain,fadein}/` and the shared model is in `internal/model/`. # Usage Source: https://docs.screenjson.com/tools/export/usage Complete flag reference and end-to-end examples for screenjson-export. ## Synopsis ``` screenjson-export -i [options] ``` ## Flags | Flag (short / long) | Default | Description | | - | - | - | | `-i` · `--input ` | — | Input file. **Required.** Extension determines format unless `-f` is set. | | `-o` · `--output ` | stdout | Output file path. | | `-f` · `--format ` | auto | Force `fdx`, `fountain`, or `fadein`. | | `--lang ` | `en` | BCP 47 primary language tag. Written to the output's `lang` field and becomes the key for every extracted text map. | | `--pretty` | `true` | Pretty-print the JSON output with two-space indentation. | | `-v` · `--version` | — | Print version and exit. | | `-h` · `--help` | — | Print help and exit. | ## Format detection Auto-detected from extension: | Extension | Format | | - | - | | `.fdx` | `fdx` | | `.fountain`, `.spmd` | `fountain` | | `.fadein` | `fadein` | Unrecognised extensions require `-f`. ## Examples ```bash theme={null} # FDX → ScreenJSON (stdout) screenjson-export -i screenplay.fdx # Write to file screenjson-export -i screenplay.fdx -o screenplay.json # Fountain, auto-detected screenjson-export -i draft.fountain -o draft.json # FadeIn, auto-detected screenjson-export -i episode.fadein -o episode.json # Force format on a renamed file screenjson-export -i script.txt --format fountain -o script.json # Pipe into jq screenjson-export -i screenplay.fdx | jq '.title.en' # French as primary language screenjson-export -i script.fdx --lang fr -o script.json # Minified JSON (no pretty-print) screenjson-export -i screenplay.fdx --pretty=false -o screenplay.min.json ``` ## Docker ```bash theme={null} # Single conversion, current directory mounted docker run --rm -v "$PWD:/data" screenjson/export \ -i /data/screenplay.fdx -o /data/screenplay.json # Pipe through jq docker run --rm -v "$PWD:/data" screenjson/export -i /data/script.fdx \ | jq '.document.scenes | length' ``` ## Exit codes | Code | Meaning | | - | - | | `0` | Success. | | `1` | Runtime error (unreadable file, parse error, unsupported format). | | `2` | Usage error (missing `-i`, bad flag). | ## Output shape The output is a ScreenJSON document with `generator` set to: ```json theme={null} { "generator": { "name": "screenjson-export", "version": "v1.0.0" } } ``` # Configuration Source: https://docs.screenjson.com/tools/greenlight/configuration Every environment variable Greenlight reads, grouped by subsystem. Greenlight loads configuration from `backend/.env` (via `godotenv`), then overlays any shell environment. Copy `env.example` as a starting point. ## Core | Variable | Default | Description | | - | - | - | | `UI_DIST_DIR` | `/app/ui` | Path to the compiled Svelte frontend. | | `UI_ENABLED` | `true` | Serve the SPA from the Go binary. | | `WORKER_ID` | auto | Overrides the auto-generated worker id. | | `MAX_WORKERS` | `4` | Worker pool size. | | `JOB_TIMEOUT` | `30m` | Per-job timeout. | | `HTTP_PORT` | `8080` | Listen port. | ## Authentication Enable auth by setting any of these. Unset ⇒ open. | Variable | Purpose | | - | - | | `GREENLIGHT_USERNAME` / `GREENLIGHT_PASSWORD` | HTTP basic auth. | | `SHARED_API_SECRET` | Enables bearer token auth. | | `SHARED_API_KEY` | Expected token value. | ## ScreenJSON binary | Variable | Default | Description | | - | - | - | | `SCREENJSON_BIN` | `/usr/local/bin/screenjson` | Path to the `screenjson` CLI binary invoked per task. | | `PDFTOHTML_BIN` | `pdftohtml` | Path to Poppler's `pdftohtml` for PDF tasks. | ## Queue | Variable | Default | Description | | - | - | - | | `QUEUE_BACKEND` | `redis` | Queue backend. Only `redis` currently supported. | | `REDIS_ADDR` | `redis:6379` | Redis server. | | `REDIS_PASSWORD` | — | Redis auth. | | `REDIS_INPUT_QUEUE` | `greenlight_jobs_waiting` | Redis list for incoming jobs. | | `REDIS_RESULTS_QUEUE` | `greenlight_jobs_finished` | Redis list for job results. | | `REDIS_RESULTS_TTL` | `2` (hours) | TTL on stored results. | ## Storage | Variable | Default | Description | | - | - | - | | `STORAGE_BACKEND` | `minio` | One of `minio`, `s3`, `noop`. | | `S3_ENDPOINT` | `http://minio:9000` | S3 API endpoint. | | `S3_REGION` | `us-west-2` | Region. | | `S3_USE_SSL` | `false` | TLS. | | `S3_FORCE_PATH_STYLE` | `true` | Required for MinIO. | | `S3_ACCESS_KEY_ID` | `minioadmin` | — | | `S3_SECRET_ACCESS_KEY` | `minioadmin` | — | | `S3_INPUT_BUCKET` | `greenlight` | Bucket watched for incoming files. | | `S3_OUTPUT_BUCKET` | `greenlight` | Bucket for results. | | `S3_INPUT_PREFIX` | `screenplays/input` | Prefix filter. | | `S3_OUTPUT_PREFIX` | `screenplays/output` | Output prefix. | ## Webhooks | Variable | Default | Description | | - | - | - | | `DEFAULT_WEBHOOKS` | — | Comma-separated webhook names applied to every job. | | `DEFAULT_WEBHOOK_URLS` | — | Comma-separated URLs applied to every job. | | `WEBHOOK_TIMEOUT` | `15s` | Outbound request timeout. | | `WEBHOOK_MAX_RETRY` | `4` | Retry cap. | | `WEBHOOK_RETRY_MIN_WAIT` | `1s` | Initial back-off. | | `WEBHOOK_RETRY_MAX_WAIT` | `30s` | Back-off cap. | | `WEBHOOK_SECRET` | `secret` | HMAC signing secret. | ## Notifications (optional multi-driver) | Variable | Purpose | | - | - | | `NOTIFY_QUEUE_SIZE` | Internal queue. Default `256`. | | `NOTIFY_TIMEOUT` | Per-driver timeout. Default `8s`. | | `NOTIFY_MAX_RETRY` | Retry cap. Default `4`. | | `NOTIFY_RETRY_MIN_WAIT` / `NOTIFY_RETRY_MAX_WAIT` | Back-off range. | | `NOTIFY_SLACK_WEBHOOK_URLS` | Slack webhook URLs (comma-separated). | | `NOTIFY_TEAMS_WEBHOOK_URLS` | Microsoft Teams. | | `NOTIFY_DISCORD_WEBHOOK_URLS` | Discord. | | `NOTIFY_FCM_SERVER_KEY` / `NOTIFY_FCM_DEVICE_TOKENS` | Firebase Cloud Messaging. | | `NOTIFY_APNS_*` | Apple Push Notification service — key id, team id, bundle id, private key / key file, device tokens, production toggle. | | `NOTIFY_SNS_REGION` / `NOTIFY_SNS_TOPIC_ARNS` | AWS SNS. | | `NOTIFY_WEBPUSH_*` | WebPush — subject, VAPID public / private keys, subscription JSON. | ## Configuration dump Every deployment logs a `CONFIG DUMP` banner on startup containing the resolved values (secrets are logged as their presence, not content). Check the first 200 lines of the container log after boot. # Deploy Source: https://docs.screenjson.com/tools/greenlight/deploy Deploy Greenlight with Docker Compose, standalone, or on Kubernetes. ## Prerequisites * Docker (for the compose path) or Go 1.22+ and Node 20+ (for source builds). * Redis instance reachable by every worker. * S3-compatible object store (AWS S3, MinIO, or `noop` for stateless runs). * The `screenjson` binary — either baked into the Greenlight image (the default Dockerfile copies it in) or mounted from `SCREENJSON_BIN`. ## Docker Compose (fastest) ```bash theme={null} git clone https://github.com/screenjson/greenlight.git cd greenlight cp env.example backend/.env docker build -f deployment/Dockerfile -t screenjson/greenlight:latest . cd deployment docker-compose up ``` The provided `docker-compose.yml` boots: * Greenlight on `:19000` (maps internal `:8080`) * Redis * MinIO on `:9000` (S3 API) and `:9001` (console) Drop files into the MinIO `greenlight/screenplays/input/` prefix — the worker picks them up. ## Standalone backend ```bash theme={null} cd backend cp ../env.example .env # or backend/.env go run cmd/greenlight/main.go ``` Watches `backend/.env`, `config/tasks.yml`, and any environment variables set before startup. ## Frontend dev server ```bash theme={null} cd frontend npm install npm run dev ``` Runs on port `5173` (Vite default). The production Svelte build lives at `backend/greenlight/` (the `UI_DIST_DIR`) and is served by the Go binary in one process. ## Kubernetes Greenlight scales horizontally. Each replica pulls from the shared Redis queue. Recommended pattern: ``` Deployment: greenlight (replicas: 3-8) env: - QUEUE_BACKEND=redis - REDIS_ADDR=redis.default.svc:6379 - STORAGE_BACKEND=s3 - S3_INPUT_BUCKET=... - S3_OUTPUT_BUCKET=... command: ["/app/greenlight"] StatefulSet: redis Service: greenlight (ClusterIP on 8080) Ingress: docs.example.com → greenlight ``` Sizing: each worker runs one conversion at a time. `MAX_WORKERS=4` per replica is a sensible starting point (matches the default). Scale replicas horizontally. ## Health endpoints | Path | Purpose | | - | - | | `GET /up` | Liveness — returns 200 as long as the HTTP server is up. | | `GET /setup` | Configuration snapshot. | | `GET /workers` | Per-worker live status. | | `GET /tail` | Recent log lines. | # Notifications Source: https://docs.screenjson.com/tools/greenlight/notifications Multi-driver notification fan-out — Slack, Teams, Discord, FCM, APNs, SNS, WebPush. Beyond [webhooks](/tools/greenlight/webhooks), Greenlight can fan job-lifecycle events out to a set of human-facing notification channels. Each driver is optional; enable by setting the relevant environment variables. ## Runtime shape | Variable | Default | Description | | - | - | - | | `NOTIFY_QUEUE_SIZE` | `256` | Internal bounded buffer; beyond this, events are dropped with a warning log. | | `NOTIFY_TIMEOUT` | `8s` | Per-driver request timeout. | | `NOTIFY_MAX_RETRY` | `4` | Retry cap. | | `NOTIFY_RETRY_MIN_WAIT` | `1s` | Initial back-off. | | `NOTIFY_RETRY_MAX_WAIT` | `30s` | Max back-off. | ## Slack ```bash theme={null} NOTIFY_SLACK_WEBHOOK_URLS=https://hooks.slack.com/services/T000/B000/xxxx,https://hooks.slack.com/services/T111/B111/yyyy ``` Comma-separated list. Every URL receives every event. ## Microsoft Teams ```bash theme={null} NOTIFY_TEAMS_WEBHOOK_URLS=https://outlook.office.com/webhook/... ``` ## Discord ```bash theme={null} NOTIFY_DISCORD_WEBHOOK_URLS=https://discord.com/api/webhooks/... ``` ## Firebase Cloud Messaging Mobile push on Android (and iOS if you've set up FCM there). | Variable | Description | | - | - | | `NOTIFY_FCM_SERVER_KEY` | FCM legacy server key. | | `NOTIFY_FCM_DEVICE_TOKENS` | Comma-separated device tokens. | ## Apple Push Notification service (APNs) iOS push. | Variable | Description | | - | - | | `NOTIFY_APNS_KEY_ID` | APNs key id. | | `NOTIFY_APNS_TEAM_ID` | Apple Developer team id. | | `NOTIFY_APNS_BUNDLE_ID` | Topic bundle id. | | `NOTIFY_APNS_PRIVATE_KEY` | PEM key inline. | | `NOTIFY_APNS_PRIVATE_KEY_FILE` | PEM key file path (alternative to inline). | | `NOTIFY_APNS_DEVICE_TOKENS` | Comma-separated device tokens. | | `NOTIFY_APNS_PRODUCTION` | `true` for the production APNs environment; `false` (default) for sandbox. | ## AWS SNS Event fan-out to SNS topics. | Variable | Description | | - | - | | `NOTIFY_SNS_REGION` | AWS region. | | `NOTIFY_SNS_TOPIC_ARNS` | Comma-separated topic ARNs. | ## WebPush Browser push (Chrome, Firefox, Edge, Safari 16+). | Variable | Description | | - | - | | `NOTIFY_WEBPUSH_SUBJECT` | `mailto:...` or URL the subscriber can contact. | | `NOTIFY_WEBPUSH_PUBLIC_KEY` | VAPID public key. | | `NOTIFY_WEBPUSH_PRIVATE_KEY` | VAPID private key. | | `NOTIFY_WEBPUSH_SUBSCRIPTIONS_JSON` | JSON array of push subscription objects (or a path to a file). | ## Delivery semantics * All drivers are fire-and-forget from the worker's perspective. * A dedicated goroutine pool drains the notification queue. * Failures retry up to `NOTIFY_MAX_RETRY` times with the configured back-off; then they are logged and dropped. * No ordering guarantees across drivers — the same event may reach Slack before Teams, or vice versa. # Overview Source: https://docs.screenjson.com/tools/greenlight/overview Greenlight is a Go + Svelte job queue microservice that wraps screenjson-cli for batch operations. Greenlight is a **job queue manager** for the ScreenJSON toolchain. It ingests screenplays from object storage, runs pipelines of tasks against the `screenjson` binary, and writes results back. ## Architecture ``` S3 / MinIO ingest → Redis queue → Worker supervisor → screenjson CLI → S3 / MinIO output ``` Components: * **Backend** — Go HTTP server (port 8080 by default) with a worker pool and a supervised `worker.Loop` per CPU. * **Queue** — Redis, with configurable input / results lists. * **Storage** — S3-compatible object store (AWS S3, MinIO, or `noop` for local dev). * **Engine** — wraps the `screenjson` CLI binary. * **Task executor** — runs declarative task pipelines defined in `config/tasks.yml`. * **Frontend** — Svelte + TailwindCSS SPA served by the Go binary. Job list, live progress, submission, task catalogue. * **Notifications** — optional fan-out to Slack, Teams, Discord, FCM, APNs, SNS, WebPush. ## What you can do with it * Ingest a bucket of `.fdx` files nightly, convert them all to ScreenJSON, validate, and file the output. * Expose a REST submission endpoint to other services — jobs come in, get queued, get processed, get webhooked back out. * Compose pipelines of tasks (convert → validate → encrypt → zip) without writing bash. * Run the same binary as your `screenjson serve` instance, but queued, retry-backed, and observable. ## Not in scope * Not a website CMS. UI is for operators, not end users. * Not a multi-tenant billing platform. Single tenant per deployment. * No built-in authentication UI — it does basic auth and a shared bearer token if configured. ## Source Private repository, under commercial licence. See [Deploy](/tools/greenlight/deploy) for all the runtime prerequisites. # Pipelines Source: https://docs.screenjson.com/tools/greenlight/pipelines Compose tasks into ordered pipelines submitted via POST /push. A **pipeline** is an ordered list of task names submitted to the queue as a single job. Tasks run sequentially against the output of the previous step. ## Submission shape ```json theme={null} { "input_key": "screenplays/input/script.fdx", "output_key": "screenplays/output/script-v1", "document_id": "12345", "tasks": ["convert-fdx-to-screenjson", "validate-screenjson"], "vars": { "encrypt_key": "secret" }, "webhook": "https://example.com/callback", "tenant_id": "acme" } ``` Submitted to [`POST /push`](/api-reference/greenlight/endpoint/push-job). ## Fields | Field | Type | Required | Purpose | | - | - | - | - | | `input_key` | string | ✓ | Object key of the source file in `S3_INPUT_BUCKET`. | | `output_key` | string | ✓ | Base prefix for all pipeline outputs in `S3_OUTPUT_BUCKET`. | | `document_id` | string | ✓ | Your own identifier. Included in events and webhook payloads. | | `tasks` | string\[] | ✓ | One or more task names from [the catalogue](/tools/greenlight/tasks). | | `vars` | object | — | Task variables — e.g. `encrypt_key`. Merged with task-defined `params`. | | `webhook` | string | — | Per-job webhook URL. Combined with `DEFAULT_WEBHOOK_URLS`. | | `tenant_id` | string | — | Arbitrary tenant tag; surfaces in metrics and webhook payloads. | ## Example pipelines ### FDX → validated ScreenJSON ```json theme={null} { "input_key": "screenplays/input/script.fdx", "output_key": "screenplays/output/script", "document_id": "S1E04", "tasks": ["convert-fdx-to-screenjson", "validate-screenjson"] } ``` ### ScreenJSON → multi-format ZIP ```json theme={null} { "input_key": "screenplays/input/script.json", "output_key": "screenplays/output/bundle", "document_id": "S1E04", "tasks": [ "export-to-fdx", "export-to-fountain", "export-to-pdf", "zip-output" ] } ``` ### Convert from Fountain, encrypt for distribution ```json theme={null} { "input_key": "screenplays/input/script.fountain", "output_key": "screenplays/output/encrypted", "document_id": "S1E04", "tasks": ["convert-fountain-to-screenjson", "encrypt-screenjson"], "vars": { "encrypt_key": "mysecretkey123" } } ``` ## How it runs 1. A worker claims the job from Redis (`REDIS_INPUT_QUEUE`). 2. It downloads `input_key` from object storage. 3. For each task in order, it invokes `greenlight.` against the current working file. Outputs land in `{output_key}/{task.output_dir}/{stem}.{ext}`. 4. Results are written to `REDIS_RESULTS_QUEUE` with TTL `REDIS_RESULTS_TTL` hours. 5. Webhooks fire (per-job and defaults). 6. Notifications fan out to any configured drivers. Failed tasks abort the pipeline; outputs already written stay in place. ## Status and results Track progress with: * [`GET /results`](/api-reference/greenlight/endpoint/list-results) — paginated list of recent jobs. * [`GET /results/{id}`](/api-reference/greenlight/endpoint/get-result) — one specific job, including per-task timings and output keys. * [`GET /workers`](/api-reference/greenlight/endpoint/get-workers) — live worker status. # Storage Source: https://docs.screenjson.com/tools/greenlight/storage S3 / MinIO / noop storage backends and key layout. Greenlight reads source files from an S3-compatible bucket, writes results back to an output bucket, and supports three backend drivers. ## Backends ### `minio` (default) For local dev and on-prem deployments. `docker-compose` boots MinIO automatically and seeds matching credentials. ```bash theme={null} STORAGE_BACKEND=minio S3_ENDPOINT=http://minio:9000 S3_FORCE_PATH_STYLE=true S3_ACCESS_KEY_ID=minioadmin S3_SECRET_ACCESS_KEY=minioadmin ``` ### `s3` AWS S3 or any other S3-compatible service. ```bash theme={null} STORAGE_BACKEND=s3 S3_ENDPOINT= S3_REGION=us-west-2 S3_USE_SSL=true S3_FORCE_PATH_STYLE=false S3_ACCESS_KEY_ID=... S3_SECRET_ACCESS_KEY=... ``` Leave `S3_ENDPOINT` blank for AWS S3 proper. Set `S3_FORCE_PATH_STYLE=true` for any third-party service that requires path-style addressing. ### `noop` Disables storage entirely. Useful for pure-stdin / stdout pipelines or integration tests. ```bash theme={null} STORAGE_BACKEND=noop ``` ## Key layout ### Input ``` {S3_INPUT_BUCKET}/{S3_INPUT_PREFIX}/...your files... ``` Default: `greenlight/screenplays/input/`. Submission references the full object key relative to the bucket: ```json theme={null} { "input_key": "screenplays/input/script.fdx" } ``` ### Output ``` {S3_OUTPUT_BUCKET}/{output_key}/{task.output_dir}/{stem}.{ext} ``` `output_key` is supplied per-job; `task.output_dir` is defined per task in `config/tasks.yml`. Example, submitting with `output_key: "screenplays/output/script-v1"` and `tasks: ["convert-fdx-to-screenjson", "validate-screenjson"]` produces: ``` greenlight/screenplays/output/script-v1/json/script.json greenlight/screenplays/output/script-v1/validation/script_validation.json ``` ## Browsing storage The service exposes [`GET /storage`](/api-reference/greenlight/endpoint/list-storage) to list buckets and objects directly. Convenient for debugging but usually not exposed publicly — put it behind auth. ## IAM (AWS S3) Minimum permissions for a dedicated service account: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket", "s3:DeleteObject"], "Resource": [ "arn:aws:s3:::", "arn:aws:s3:::/*" ] }, { "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::/*" } ] } ``` ## Large files Default `JOB_TIMEOUT=30m` handles multi-hundred-megabyte PDFs comfortably. For unusually large source material, raise the timeout and scale worker replicas horizontally; no single worker is sharded per task. # Task catalogue Source: https://docs.screenjson.com/tools/greenlight/tasks Every task Greenlight ships with, from config/tasks.yml. Tasks are declared in `config/tasks.yml`. Each task records its accepted input extensions, the binary it requires, the handler used to run it, the output directory, and the output filename template. ## Import ### `convert-fdx-to-screenjson` Converts Final Draft (`.fdx`) to ScreenJSON. | Field | Value | | - | - | | Accepts | `.fdx` | | Requires | `/usr/local/bin/screenjson` | | Handler | `greenlight.convert` (`format: fdx`) | | Output dir | `json` | | Output | `{stem}.json` | ### `convert-fountain-to-screenjson` Converts Fountain (`.fountain`) plain-text screenplay to ScreenJSON. | Field | Value | | - | - | | Accepts | `.fountain` | | Requires | `screenjson` | | Handler | `greenlight.convert` (`format: fountain`) | | Output dir | `json` | | Output | `{stem}.json` | ### `convert-fadein-to-screenjson` Converts FadeIn Pro (`.fadein`) to ScreenJSON. | Field | Value | | - | - | | Accepts | `.fadein` | | Requires | `screenjson` | | Handler | `greenlight.convert` (`format: fadein`) | | Output dir | `json` | | Output | `{stem}.json` | ### `convert-pdf-to-screenjson` Extracts screenplay structure from a PDF via Poppler and converts to ScreenJSON. Best-effort for scanned / image PDFs. | Field | Value | | - | - | | Accepts | `.pdf` | | Requires | `screenjson`, `pdftohtml` | | Handler | `greenlight.convert` (`format: pdf`) | | Output dir | `json` | | Output | `{stem}.json` | ## Export ### `export-to-fdx` | Field | Value | | - | - | | Accepts | `.json`, `.yaml`, `.yml` | | Handler | `greenlight.export` (`format: fdx`) | | Output dir | `fdx` | | Output | `{stem}.fdx` | ### `export-to-fountain` | Field | Value | | - | - | | Accepts | `.json`, `.yaml`, `.yml` | | Handler | `greenlight.export` (`format: fountain`) | | Output dir | `fountain` | | Output | `{stem}.fountain` | ### `export-to-fadein` | Field | Value | | - | - | | Accepts | `.json`, `.yaml`, `.yml` | | Handler | `greenlight.export` (`format: fadein`) | | Output dir | `fadein` | | Output | `{stem}.fadein` | ### `export-to-pdf` Renders the document as a screenplay PDF using the native encoder. | Field | Value | | - | - | | Accepts | `.json`, `.yaml`, `.yml` | | Handler | `greenlight.export` (`format: pdf`, `paper_size: letter`, `font: courier`) | | Output dir | `pdf` | | Output | `{stem}.pdf` | ## Validation ### `validate-screenjson` Schema-checks a document and emits a JSON report. | Field | Value | | - | - | | Accepts | `.json` | | Handler | `greenlight.validate` | | Output dir | `validation` | | Output | `{stem}_validation.json` | ## Encryption ### `encrypt-screenjson` AES-256 encrypts every text run. Requires an `encrypt_key` job variable. | Field | Value | | - | - | | Accepts | `.json` | | Handler | `greenlight.encrypt` | | Output dir | `encrypted` | | Output | `{stem}_encrypted.json` | | Required `vars` | `encrypt_key` | ### `decrypt-screenjson` Decrypts. Requires the same `encrypt_key` used to encrypt. | Field | Value | | - | - | | Accepts | `.json` | | Handler | `greenlight.decrypt` | | Output dir | `decrypted` | | Output | `{stem}_decrypted.json` | | Required `vars` | `encrypt_key` | ## Packaging ### `zip-output` ZIPs the entire output directory. | Field | Value | | - | - | | Accepts | any | | Requires | `/usr/bin/zip` | | Handler | `cli` | | Command | `cd {output_dir} && zip -r package.zip . -x package.zip` | | Output | `package.zip` | ## Adding tasks `config/tasks.yml` is loaded on boot. New entries follow this schema: ```yaml theme={null} - name: my-task description: Short label human: Longer explanation shown in the UI icon: mdi:something accept: [.json] requires: - { type: binary, path: /usr/local/bin/screenjson, name: screenjson } handler: greenlight.convert # or cli output_dir: output params: { format: fdx } job_vars: [optional_var_names] cmd: "shell command (only when handler is cli)" output: - "{stem}.ext" ``` Restart the service to pick up changes. # Webhooks Source: https://docs.screenjson.com/tools/greenlight/webhooks Outbound HTTP webhooks fire on job lifecycle events. Greenlight fires outbound HTTP webhooks for every job lifecycle event. Both per-job and deployment-wide defaults are supported. ## Configuration ### Deployment defaults Apply to every job. | Variable | Description | | - | - | | `DEFAULT_WEBHOOKS` | Comma-separated named webhook ids. | | `DEFAULT_WEBHOOK_URLS` | Comma-separated raw URLs. | ### Per-job On submission, pass a `webhook` field. Combined additively with the defaults. ```json theme={null} { "webhook": "https://example.com/hooks/greenlight" } ``` ### Request behaviour | Variable | Default | Description | | - | - | - | | `WEBHOOK_TIMEOUT` | `15s` | Per-request timeout. | | `WEBHOOK_MAX_RETRY` | `4` | Max retries before giving up. | | `WEBHOOK_RETRY_MIN_WAIT` | `1s` | Initial back-off. | | `WEBHOOK_RETRY_MAX_WAIT` | `30s` | Max back-off. | | `WEBHOOK_SECRET` | `secret` | HMAC-SHA256 signing secret. | ## Event payload Each POST carries `Content-Type: application/json`. Payload shape: ```json theme={null} { "event": "job.completed", "job_id": "j_01HAYP...", "document_id": "12345", "tenant_id": "acme", "tasks": ["convert-fdx-to-screenjson", "validate-screenjson"], "status": "success", "started_at": "2026-01-14T10:30:00Z", "finished_at": "2026-01-14T10:30:12Z", "outputs": [ { "task": "convert-fdx-to-screenjson", "keys": ["screenplays/output/script/json/script.json"] }, { "task": "validate-screenjson", "keys": ["screenplays/output/script/validation/script_validation.json"] } ], "error": null } ``` ## Event kinds | `event` | Fires | | - | - | | `job.queued` | Immediately after submission. | | `job.started` | When a worker claims the job. | | `job.progress` | Between tasks. Body includes the completed task list so far. | | `job.completed` | On successful pipeline finish. | | `job.failed` | On any task failure. `error` is populated. | ## Signing Every request carries an `X-Greenlight-Signature: sha256=` header over the raw request body, using `WEBHOOK_SECRET`. Verify in your receiver: ```js theme={null} import crypto from 'node:crypto'; function verify(rawBody, signatureHeader, secret) { const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader)); } ``` ## Webhook status The [`GET /webhooks`](/api-reference/greenlight/endpoint/get-webhooks) endpoint returns the current webhook configuration and recent delivery attempts. # Data attributes Source: https://docs.screenjson.com/tools/ui/data-attributes Configure the CDN-embedded viewer from attributes on the script tag — no JavaScript required. When you drop the CDN build into a page with a `data-src` attribute, the library auto-mounts a viewer for you — no explicit `new ScreenJSONUI(...)` call needed. ## Minimum viable embed ```html theme={null} ``` ## All supported attributes Every attribute is optional unless noted. | Attribute | Type | Equivalent config | Notes | | - | - | - | - | | `data-src` (**required**) | string | `src` | URL of the ScreenJSON document. `data-source` is accepted as an alias. | | `data-theme` | `light` \| `dark` | `theme` | Initial theme. Defaults to `light`. | | `data-zoom` | number | `zoom` | Parsed as a float. | | `data-lang` | BCP 47 | `lang` | Override the rendered language. | | `data-virtual` | `true` \| `false` | `virtual` | Virtual scrolling. | | `data-width` | CSS length | — | Container width. Numeric values get `px`; `%` suffix preserved. | | `data-height` | CSS length | — | Container height. Numeric values get `px`; `%` suffix preserved. | The auto-mount only fires for script tags whose `src` matches `*screenjson*` and that carry `data-src`. ## Query parameters Alternatively, a `?src=` parameter on the current page URL is also picked up if no `src` is supplied in config. Useful for embedding a viewer on a dedicated page: ``` https://viewer.example.com/?src=https://cdn.example.com/screenplay.json&theme=dark ``` # Encrypted documents Source: https://docs.screenjson.com/tools/ui/encrypted Open encrypted ScreenJSON documents in the viewer. When a document's `encrypt` block is set, the viewer can decrypt text runs in-browser before rendering. Structure, UUIDs, and metadata stay visible regardless. ## Detection The loader calls `hasEncryptedContent(doc)` on every load. If encrypted content is detected, one of two flows runs: 1. **Password in config** — if `password` was passed in config, it's used to decrypt in-memory and the viewer renders directly. 2. **Password modal** — otherwise a `PasswordModal` appears, prompting the user. Wrong passwords trigger an inline error and re-prompt. ## JavaScript ```ts theme={null} import ScreenJSONUI from 'screenjson-ui'; import 'screenjson-ui/style.css'; new ScreenJSONUI({ element: 'viewer', src: '/encrypted.json', password: 'SharedSecret123' }); ``` ## Svelte ```svelte theme={null} ``` ## Programmatic helpers Imported from the library: ```ts theme={null} import { hasEncryptedContent, decryptDocument, decryptText } from 'screenjson-ui'; const doc = await fetch('/encrypted.json').then((r) => r.json()); if (hasEncryptedContent(doc)) { decryptDocument(doc, 'SharedSecret123'); // mutates in place } ``` | Helper | Signature | Purpose | | - | - | - | | `hasEncryptedContent(doc)` | `(doc: ScreenJSONDocument) => boolean` | Detect encryption on any element in the document. | | `decryptDocument(doc, key)` | mutates in place | Decrypt every encrypted text run in the document tree. | | `decryptText(cipher, key, encoding?)` | returns plain text | Decrypt a single AES-256-CTR text run. | ## Errors When a wrong password is supplied, the viewer calls `onError` with a `LoaderError` carrying `code: 'DECRYPT_FAILED'`. The modal flow retries until the user cancels or succeeds. ## What's encrypted (recap) ScreenJSON's encryption block targets text runs only — UUIDs, scene counts, character names, revision metadata, and `meta` stay in plain text. See the [Encryption specification](/specification/encryption) for exactly which fields are touched. # Install Source: https://docs.screenjson.com/tools/ui/install Install screenjson-ui from npm or via the CDN script tag. ## npm ```bash theme={null} npm install screenjson-ui ``` ```ts theme={null} import ScreenJSONUI from 'screenjson-ui'; import 'screenjson-ui/style.css'; ``` Package metadata: | Field | Value | | - | - | | `name` | `screenjson-ui` | | `version` | `1.0.0` | | `module` | `./dist/screenjson-ui.js` (ES) | | `main` | `./dist/screenjson-ui.umd.js` (UMD) | | `types` | `./dist/index.d.ts` | | `exports["./style.css"]` | Pre-compiled stylesheet — `import 'screenjson-ui/style.css'`. | | `sideEffects` | `["*.css"]` so bundlers don't tree-shake the styles. | ## CDN Single ES-module bundle plus a stylesheet: ```html theme={null} ``` Alternatively, import the ESM URL directly inside a module script: ```html theme={null} ``` ## Hosted viewer If you only need to read a single document and don't want to host the viewer yourself, the same library is mounted at [`screenjson.com/viewer/`](https://screenjson.com/viewer/) — drop a file or paste a URL. Deep-link with `?src=`: ``` https://screenjson.com/viewer/?src=https://example.com/screenplay.json ``` ## Fonts The library uses **Courier Prime**, loaded by `app.css` from Google Fonts: ```css theme={null} @import url("https://fonts.googleapis.com/css2?family=Courier+Prime:wght@400;700&display=swap"); ``` If you prefer to self-host, install the font separately and override `--font-screenplay` in your own CSS: ```css theme={null} :root { --font-screenplay: "Courier Prime", "Courier New", monospace; } ``` ## TypeScript Types ship with the npm package: ```ts theme={null} import ScreenJSONUI, { type ScreenJSONUIConfig, type ScreenJSONDocument, type Theme, type Lang } from 'screenjson-ui'; ``` # JavaScript API Source: https://docs.screenjson.com/tools/ui/javascript Embed screenjson-ui via the ScreenJSONUI class. ```ts theme={null} import ScreenJSONUI, { type ScreenJSONUIConfig } from 'screenjson-ui'; import 'screenjson-ui/style.css'; const viewer = new ScreenJSONUI({ element: 'viewer', // an id or HTMLElement src: '/screenplay.json', theme: 'dark', onLoad: (doc) => console.log('loaded', doc.title), onPageChange: (page) => console.log('page', page) }); ``` ## Constructor ```ts theme={null} new ScreenJSONUI(config: ScreenJSONUIConfig) ``` The constructor resolves `config.element` immediately and begins initialisation asynchronously. ### `element: string | HTMLElement` Either an element id (without the `#`) or an `HTMLElement` reference. If the id does not resolve, the constructor throws `Element not found: `. ### `src: string` URL of a ScreenJSON document. The internal `DocumentLoader` fetches and parses it. Failures call `onError` with a structured `LoaderError`. ### `document: ScreenJSONDocument` A pre-loaded document. Takes precedence over `src` when both are provided — useful when you've already fetched and validated the doc. ## Loading precedence When no `document` is passed, the loader looks for a source in this order: 1. `config.src` 2. `?src=` query parameter on the host page URL 3. `data-src` / `data-source` on the currently-executing ` ``` See [Options](/tools/ui/options) for the complete config surface. # Configuration options Source: https://docs.screenjson.com/tools/ui/options The complete ScreenJSONUIConfig reference. Every viewer — whether embedded via `new ScreenJSONUI(...)`, auto-mounted from a CDN script tag, or used as a Svelte component — supports the same option set. ## `ScreenJSONUIConfig` | Option | Type | Default | Description | | - | - | - | - | | `element` | `string \| HTMLElement` | — **required** | Mount point. String is treated as an `id`. | | `src` | `string` | — | URL to fetch the ScreenJSON document from. | | `document` | `ScreenJSONDocument` | — | Pre-loaded document. Takes precedence over `src`. | | `theme` | `'light' \| 'dark'` | system preference | Initial theme. Persisted to `localStorage['screenjson-theme']`. | | `zoom` | `number` | `1` | Manual multiplier on top of auto-fit. 0.5 – 2. | | `lang` | `Lang` | document `lang` | BCP 47 tag — chooses which language to render. | | `numbered` | `boolean` | `false` | Show scene numbers in the margin. | | `paginated` | `boolean` | `true` | Show page numbers. | | `password` | `string` | — | Password for encrypted content. If omitted on an encrypted document, the viewer opens a `PasswordModal`. | | `showMenu` | `boolean` | `true` | Render the built-in sticky toolbar (theme, zoom, language, info, print, download). | | `class` | `string` | — | Extra CSS class applied to the outer wrapper. | | `onLoad` | `(doc: ScreenJSONDocument) => void` | — | Called once on successful load. | | `onPageChange` | `(page: number) => void` | — | Called on every page change. | | `onError` | `(err: LoaderError) => void` | — | Called on fetch / parse / decrypt error. | ## `Theme` ```ts theme={null} type Theme = 'light' | 'dark'; ``` Theme resolution order at boot: 1. `config.theme` 2. `localStorage['screenjson-theme']` 3. `prefers-color-scheme` 4. `'light'` (final fallback) ## `Lang` A BCP 47 language tag string — `en`, `en-GB`, `fr-CA`, `ja`, `zh-Hant`, `sr-Latn-RS`. The viewer's language picker only appears when the document declares more than one. ## Auto-fit zoom The viewer measures its container width and shrinks the paper to fit when the viewport is narrower than the natural 8.5″ page. Your `zoom` config is a multiplier on top of that fit: ``` effectiveZoom = clamp(0.3, autoFit * zoom, 2) ``` Toolbar zoom buttons adjust `zoom` in 10% steps. ## CSS variables Every visual token is a CSS custom property declared on `:root` (or on `html.dark` for the dark variant). Override any of them in your own stylesheet to re-theme: ```css theme={null} :root { --color-paper: #fffdf6; /* warm paper */ --color-paper-fg: #1a1a16; --color-workspace: #d6d3c4; --color-brand-500: #6a1ef0; --margin-action-left: 1.7in; /* WB margins */ } ``` The complete token list is in [`app.css`](https://github.com/screenjson/screenjson-ui/blob/main/src/app.css). ## Formatting constants The viewer also exports formatting constants from `screenjson-ui/dist/...` if you want to reuse them in your own calculations: ```ts theme={null} import { PAGE_WIDTH_INCHES, // 8.5 PAGE_HEIGHT_INCHES, // 11 PAGE_WIDTH_PX, // 816 PAGE_HEIGHT_PX, // 1056 FONT_SIZE_PT, // 12 LINES_PER_INCH, // 6 MAX_LINES_PER_PAGE, // 60 MARGINS // { page, action, dialogue, character, parenthetical, transition } } from 'screenjson-ui'; ``` # Overview Source: https://docs.screenjson.com/tools/ui/overview screenjson-ui is an embeddable JavaScript viewer that renders ScreenJSON documents as Final Draft / WriterDuet-style screenplays in the browser. `screenjson-ui` renders a ScreenJSON document as a pixel-accurate, studio-quality screenplay in the browser. It ships as a small ES module + UMD bundle, drops in via npm or a CDN script tag, and exposes a native Svelte component for SvelteKit projects. The hosted viewer at screenjson.com/viewer/ embeds the same library, with a sidebar of bundled examples, file upload, and URL loader. ## Features * **Print-screenplay layout** — US Letter, Courier Prime 12pt, industry-standard margins for action, dialogue, character cues, parentheticals, shots, and transitions. * **Light and dark themes** — `html.dark` class strategy, system-preference detection, persisted in `localStorage`. * **Continuous-scroll pagination** — every page rendered in flow, top to bottom; CSS `zoom` auto-fit shrinks the paper to viewport on narrow windows, user zoom multiplies on top. * **Multi-language** — switch the rendered language at runtime via the toolbar or the `lang` config option. * **Encrypted documents** — supply a password (or open the built-in modal) to decrypt AES-256-CTR text runs in-browser. * **Built-in toolbar** — title, page count, zoom group, language, info, print, download, theme. * **Metadata panel** — overview / characters / scenes drawer. * **Tailwind v4 + CSS variables** — easy to re-theme without writing custom CSS. * **Zero framework dependency** in the embed API; native Svelte component for SvelteKit users. ## Distributions | Distribution | When to use | | - | - | | **npm** — `npm install screenjson-ui` | You're building a JS/TS app and want types + tree-shaking. | | **CDN script tag** — `https://cdn.screenjson.com/ui/screenjson-ui.js` | You want to drop the viewer into any HTML page in two lines. | | **Svelte component** — `import { ScreenJSONViewer } from 'screenjson-ui'` | You're on SvelteKit. | ## Module entry points | Import | What it is | | - | - | | `ScreenJSONUI` *(default export)* | Embed class — pass a config, it mounts a viewer into a container. | | `ScreenJSONViewer` | Svelte component for direct mounting. | | `ViewerVirtual`, `Page`, `Menu`, `MetadataPanel`, `ErrorDisplay`, `PasswordModal`, `VirtualScroller` | Individual components for custom layouts. | | `DocumentLoader`, `loadFromUrl` | Fetch + parse helpers. | | `Paginator`, `paginate`, `createPaginator` | Client-side pagination engine. | | `validateDocument`, `isScreenJSONDocument` | Shape checks. | | `hasEncryptedContent`, `decryptDocument`, `decryptText` | Encryption helpers. | | `themeStore`, `settingsStore`, `documentStore` | Svelte stores. | ## Rendering standard Matches the Warner Bros / Final Draft convention used by every major studio: * **Paper:** US Letter (8.5" × 11") * **Font:** Courier Prime, 12pt (loaded from Google Fonts; falls back to system Courier) * **Lines per page:** 60 maximum * **Margins:** Action 1.5" L / 1" R · Dialogue 2.5" L / 2" R · Character 3.7" L · Parenthetical 3.1" L / 2.9" R · Transition 5.8" L ## Browser support Chrome 90+ · Firefox 90+ · Safari 14+ · Edge 90+. Uses CSS `zoom` for paper scaling — works in all four. ## License MIT. The viewer is open source — [github.com/screenjson/screenjson-ui](https://github.com/screenjson/screenjson-ui). # Svelte component Source: https://docs.screenjson.com/tools/ui/svelte Drop ScreenJSONViewer into a Svelte or SvelteKit app. ```svelte theme={null} ``` ## Component props `ScreenJSONViewer` accepts the same options as the JavaScript constructor: | Prop | Type | Default | Description | | - | - | - | - | | `document` | `ScreenJSONDocument` | — | Pre-loaded document. | | `src` | `string` | — | URL to load from (alternative to `document`). | | `theme` | `'light' \| 'dark'` | system preference | Initial theme. | | `zoom` | `number` | `1` | Manual multiplier on top of auto-fit. 0.5 – 2. | | `lang` | `Lang` | document `lang` | Render language. | | `numbered` | `boolean` | `false` | Show scene numbers in the margin. | | `paginated` | `boolean` | `true` | Show page numbers. | | `password` | `string` | — | Decrypt password for encrypted content. | | `showMenu` | `boolean` | `true` | Render the built-in toolbar. Set to `false` if your app provides its own chrome. | ## Other exported components For custom layouts: ```ts theme={null} import { ScreenJSONViewer, ViewerVirtual, Page, Menu, MetadataPanel, ErrorDisplay, PasswordModal, VirtualScroller } from 'screenjson-ui'; ``` ## Stores Three reactive Svelte stores are exported: ```ts theme={null} import { themeStore, settingsStore, documentStore } from 'screenjson-ui'; import type { Theme } from 'screenjson-ui'; themeStore.set('dark'); ``` ## SvelteKit route example ```ts theme={null} // +page.ts export async function load({ fetch, params }) { const doc = await fetch(`/screenplays/${params.slug}.json`).then((r) => r.json()); return { doc }; } ``` ```svelte theme={null} ``` # Overview Source: https://docs.screenjson.com/tools/viewer/overview screenjson-viewer is the native ScreenJSON reader — one Tauri 2 app targeting desktop and mobile from a single codebase. `screenjson-viewer` opens ScreenJSON files as a native application. It wraps the same rendering stack as [`@screenjson/ui`](/tools/ui/overview) in a Tauri 2 shell, so a screenplay looks identical in the browser and on the desktop. Source, releases and build instructions. ## Platforms * Windows * macOS * Linux * iOS * Android ## What it does * Opens local `.json` ScreenJSON files, through the file dialog or by association with `application/vnd.screenjson+json` * Rejects invalid JSON and non-ScreenJSON files with a readable error rather than a blank page * Prompts for a password on encrypted screenplays and decrypts in memory only * Renders through `@screenjson/ui`, so pagination, themes, language switching and the metadata panel behave exactly as they do on the web * Keeps a reader-first layout that adapts from phone to desktop widths ## Install from source ```bash theme={null} git clone https://github.com/screenjson/screenjson-viewer.git cd screenjson-viewer npm install ``` The viewer takes `@screenjson/ui` from npm as an ordinary dependency, so no extra step is needed to fetch the rendering library. ## Run and build ```bash theme={null} npm run tauri:dev # run with hot reload npm run tauri:build # bundle for the current platform ``` Mobile targets use the `tauri:android:*` and `tauri:ios:*` scripts and need the corresponding platform SDK installed. ## Related The rendering library the viewer embeds. Convert screenplays into ScreenJSON to read here.