Why this works
Because ScreenJSON is text, git diff just works. Because every element
carries a UUID, changes to one scene don’t cascade into spurious diffs
on unrelated scenes. Because text is language-keyed, you can even see
translation changes as additions to a language map rather than replacements.
A reasonable repo layout
screenplays/
my-script/
manuscript.fdx # source in your writing tool
screenplay.json # canonical ScreenJSON
revisions/
2026-01-10-blue.json
2026-01-15-pink.json
notes/ # optional prose markdown
A pre-commit hook
Keep the canonical screenplay.json in sync with the source .fdx:
#!/usr/bin/env bash
# .git/hooks/pre-commit
set -e
for fdx in screenplays/**/manuscript.fdx; do
dir="$(dirname "$fdx")"
docker run --rm -v "$PWD/$dir:/data" screenjson/cli \
convert -i /data/manuscript.fdx -o /data/screenplay.json
git add "$dir/screenplay.json"
done
Pretty-printing for diffs
Commit the ScreenJSON pretty-printed — it diffs cleanly line-by-line:
screenjson convert -i manuscript.fdx | jq . > screenplay.json
Keeping UUIDs stable
Use a converter that honours existing UUIDs. Both screenjson-cli and
screenjson-export will, by default, reuse an existing
screenplay.json if one is present next to the source to keep UUIDs
stable across regenerations.
Git attributes
Mark ScreenJSON files for text diffing:
# .gitattributes
*.json diff