Script Sequencer
Script-to-animatic scaffolding for CG virtual production and cinematics in Unreal Engine. Script Sequencer builds Level Sequences from a Fountain or Final Draft (.fdx) screenplay and keeps them in sync as the script changes, without overwriting anyone’s work.
Version: 0.4.0 for Unreal Engine 5.6, 5.7, and 5.8
Platforms: Windows, Mac
Download on Fab: https://fab.com/s/d64f0cb6cf3e
Forum Post: https://forums.unrealengine.com/t/studio22-dev-script-sequencer-early-access/2843536
Support: studio22.development@gmail.com
Contents
- Quick start
- Menu
- Automation presets
- Shots and cameras
- Casting
- Speaker cameras
- Takes and captures
- Capture lists
- Recapture flags
- Mocap Manager
- Series and episodes
- Naming tokens
- Final Draft (.fdx)
- Naming and folders
- The report window
- Markers
- What the plug-in will and won’t change
- Working with the edit (EDL and OTIO)
- Working in a team
- Tips
- Data contract, tags and your own tools
- Files
- Known limitations (Early Access)
- Support
Quick Start
Requires Unreal Engine 5.8, 5.7 or 5.6 (Windows or Mac). On older engine versions, the plug-in still loads but logs a warning, and some features may not work.
Installed from Fab, the plug-in lives in your engine’s
Engine/Plugins/Marketplace/ScriptSequencer/ folder. This guide, the data contract
and three sample scripts (Sample.fountain, Sample_Shots.fountain, Sample_FDX.fdx) are in
its Docs folder. Keep scripts inside your project, for example in <Project>/Scripts/.
It is best to make a backup of your script before importing. The plug-in records each script
by its path in the project, so the same path works on every machine.
- Enable Script Sequencer in Edit > Plugins. It also enables Python Editor Script Plugin, Editor Scripting Utilities and Sequencer Scripting. Restart the editor.
- Choose how much you want automated: Tools > Script Sequencer > Automation Settings… (or Project Settings > Plugins > Script Sequencer). The default preset is Blocking, which also adds empty cine cameras. Choose Script timing if you don’t want cameras to be created.
- Choose Tools > Script Sequencer > Import > Script (Fountain or FDX)… and pick a
.fountainor.fdxfile (trySample.fountain). - For a Fountain script, decide whether ID notes such as
[[id:L-4d5e6f]]may be added. Yes keeps rewritten lines linked more reliably (a.bakcopy is saved first). Final Draft files are never changed: Final Draft 13 keeps its own IDs (see Final Draft below). - Open
/Game/Script/<Episode>/<ScriptName>_Master(<Episode>is the title page’sEpisode:value, or the script name if there isn’t one). Its Shots track holds one section per scene (sc010,sc020…), each its own Level Sequence with an empty camera (CAM_sc010) and its camera cut. Takes, EDL export/import, and Movie Render Queue work like any Unreal master sequence. - Keep editing your script. Depending on the sync mode, each save updates Sequencer straight away, or waits until you use Sync > All Scripts, or choose the script you’d like to Sync individually under the Sync menu. The report window then lists what changed.
Menu
Everything is under Tools > Script Sequencer:
- Import
- Script (Fountain or FDX) — first import, or re-link a script
- Script Folder — import a folder of scripts, one per episode, in one batch
- Sync
- All Scripts — apply every script’s changes, including ones left pending
- One entry per imported script (per episode with batch mode on) — sync the chosen script
- Sync Options
- Stop Watching Scripts — pause live updates, including after a restart
- Resume Watching Scripts — turn live updates back on
- Take Sync Ownership — make your editor the one that syncs every script on save
- Capture — Prepare Take…, Export Capture List…, Send Slates to Mocap Manager, and Match Mocap Takes
- Other
- Re-aim Placeholder Cameras — aim generated speaker placeholder cameras at placed cast
- Delete Unused Scratch Audio… — remove scratch audio files no lines use
- Remove Script — one entry per imported script: stop treating it as imported (see Removing a script)
- Automation Settings… — project-wide settings
- About Script Sequencer… — the plugin info, data contract, and engine version
Automation Presets
For teams: commit your project’s Config/DefaultEditor.ini so everyone shares settings.
| Preset | Result | Timing |
|---|---|---|
| Structure only | Shot sections and scene heading markers are created per scene | Existing timing is held |
| Script timing | A marker per line and silent scratch audio per dialogue line is also created | Estimated from the script, ripples |
| Blocking (default) | Empty cine cameras and camera cuts are also created | Estimated, ripples |
| Custom | Any combination of the settings and more | Your choice |
Sync decides when changes reach the Sequencer: Apply on every save (default) sends changes to the Sequencer automatically every time your script is saved, and Only when I choose Sync waits until you use Tools > Sync > All Scripts or select an individual script to Sync. Either way, the report window shows what each sync changed (see* The report window). A project or script still set to the old *Show changes and ask before applying option now applies on every save.
By using Python from the Unreal Output Log console, you can change the same settings (replacing <ScriptName>):
For example: import script_sequencer.editor_entry as ss; ss.configure("<ScriptName>", preset="blocking")
or ss.configure("<ScriptName>", timing="hold", sync="manual").
When an editor takes over scene timing, switch to Timing: Hold existing timing in the plug-in Automation
settings, or use timing="hold" in the Python console. The script then only updates labels and adds new material, never
retiming what exists.
Placeholder timing lengths come from word count: 2.5 words a second for dialogue (about 150 words a minute), 3.5 for action, with gaps, lead-ins, and minimum lengths, all of which you can change under Placeholder Timing on the settings page.
Shots and Cameras
- A shot note on its own line, such as
[[SHOT: MCU Anna, slow push]], starts a new shot. In Final Draft, a Shot paragraph does the same. - With Structure: one shot section per [[SHOT:]] note (chosen at import), each note becomes its own shot
sequence (
sc010_sh0010…). - With cameras on (the default Blocking preset), the plug-in adds an empty Cine Camera per shot note, with camera cuts, a sequence with no notes gets one camera for its whole length. Script timing and Structure only add no cameras, and turning cameras off later removes the generated cuts but keeps the cameras. A shot size at the start of the note (EWS, WS, MS, MCU, CU, ECU…) sets a starting focal length. In these modes cameras stay empty. Optional Per speaker aiming can place untouched placeholders with one static transform; generated cameras are never animated or deleted.
Casting
- In Project Settings > Plugins > Script Sequencer > Casting, turn on Bind cast and fill in Cast: each
character’s name as written in the script (
ANNA), and the Actor class to use, such as a Character or MetaHuman Blueprint. Extensions such as(V.O.)and(CONT'D)are ignored when matching. - Each scene sequence then gets one empty binding per character who speaks in it, named after the character, so layout starts with the cast in place. It never places, animates, changes or deletes a binding, and a binding you delete isn’t made again.
- A character with lines but no cast entry is listed in the report window, once per scene, with Open casting settings.
- If you give a character a new class, existing bindings keep the old one and the report says so. Delete a binding and sync again to get one of the new class.
- One script can add to or change the table from Python:
ss.configure("Ep01", cast={"CARL": "/Game/Characters/BP_Carl.BP_Carl_C"}). A class of""leaves that character out for that script.
Speaker cameras
- Choose Tools > Script Sequencer > Automation Settings…. Select the Custom automation preset, then Cameras: One placeholder camera per speaker and a wide.
- Sync the script. Each scene or shot sequence gets a wide camera and a camera for each on-screen
speaker. V.O., O.S. and O.C. lines hold the current camera. Dual dialogue uses the wide. Lines marked
(V.O.),(O.S.)or(O.C.)never get a speaker camera or a cut. A character literally named VO, OS or OC is treated the same way. - Under Speaker Cameras, choose Action Cut (Wide or Hold), Speaker Cut Min Seconds (1.5 by default) and Speaker Shot Size (MCU by default). A line shorter than Speaker Cut Min Seconds never gets a cut of its own: the camera before it holds through it. Shot notes and dual dialogue always cut.
- A shot note controls its whole segment, until the next note or unit end. Use a note such as
[[SHOT: CU Ben reacts]]for a reaction shot. The script cannot infer who should be listening. - To aim cameras, bind the cast, place the characters through their sequence bindings, enable Aim Cameras, then choose Tools > Script Sequencer > Re-aim Placeholder Cameras. A newly created camera can also be initially aimed when its cast is already placed.
- Re-aim writes one static template transform, without animation keys. Saving the screenplay does not re-aim existing cameras. After changing character layout, select Re-aim explicitly.
- Moving a camera, changing its lens/filmback, or adding authored camera/component tracks gives its placement to you. Editing any cut gives the sequence’s cuts to you. Deleted cameras stay deleted; changing modes keeps all camera bindings and changes only unowned generated cuts.
- Cameras from earlier versions without a verified baseline are kept as they are. A failed partial aim is protected too. Frame those manually or use a fresh generated placeholder. Aiming needs the editor UI for evaluated cast samples, and does not perform collision checks, camera moves, performance tracking or automatic reaction shots.
Takes and captures
- Take hooks work with every Take Recorder recording, including virtual-camera recordings.
- Turn on Take hooks under Project Settings > Plugins > Script Sequencer > Capture. Take Recorder must be enabled. Without it, Script Sequencer still loads and these commands explain what they need.
- Open a generated scene or shot sequence and choose Tools > Script Sequencer > Prepare Take…. Take Recorder’s
slate is set to the sequence code (
sc010), the take number to the next free one, and the description lists the line IDs the take covers. Your own notes in the description are kept. From Python:ss.prepare_take("sc010", script_name="Ep01"), or addlines=["L-4d5e6f"]for some lines only. - An operator can also type the code as the slate. When Record is pressed, the line list is filled in.
- When several scripts use the same code. Every script starts at
sc010, so in a project with more than one script, a code likesc010can belong to several of them. Type the slate with the script’s name or episode in front, such asEp02_sc010, and it always goes to that script. A plainsc010goes to the scene open in Sequencer when you press Record. If neither tells them apart, the take isn’t recorded against any lines, and the Output Log says how to type the slate. Prepare Take…, Send Slates to Mocap Manager and the capture list writeEp02_sc010slates for repeated codes by themselves.
Removing a script
- Choose Tools > Script Sequencer > Other > Remove Script and pick the script. You’re asked whether to delete the sequences and scratch sounds its syncs made as well (Yes), or keep them and only remove Script Sequencer’s records for it (No). With Yes, anything you changed inside those sequences goes with them, but recorded takes and other assets in the folder are kept, and the script file itself is never changed. Importing it again later starts afresh.
- Under source control, submit the deletion of the script’s
.sync.jsonand.changes.jsonlfiles afterward. - A script whose generated master sequence you delete by hand stops counting for slates, the Sync menu and Sync All straight away, but its records stay until you use Remove Script, so nothing is lost if the content comes back from source control.
- When a take finishes, it’s recorded against its lines:
ss.inspect("Ep01")lists each capture, the take is taggedsuite.unit,suite.linesandsuite.take, and the change journal gets a capture entry. Nothing in Sequencer changes, and the scratch audio and its claims are untouched. Cancelled takes are ignored. - Captures work from any editor, not only the one that syncs the script. If the sync record is checked out by
someone else, captures wait in
Saved/ScriptSequencer/pending_captures.jsonand are saved as soon as it’s free.ss.flush_captures()tries again at once. - If a finished take wasn’t recorded (the Output Log says capture was not queued), keep the take and run
ss.capture_take("/Game/Cinematics/Takes/sc010_001", script_name="Ep01")before rewriting its lines. - Every sync updates the script’s sync record, even when nothing in Sequencer changes, so a sync checks it out first. If a teammate has it checked out, the sync waits until it’s free.
Capture lists
- Tools > Script Sequencer > Export Capture List… writes a capture list for every script: the stage’s script reference, one section per slate (unit code), with each line’s ID, character, text, word count, estimated seconds, takes recorded so far, and whether it needs recapturing.
- Two files per script, beside its sync record in
<Project>/ScriptSequencer/:<Script>.capture.csvfor Numbers, Excel or Google Sheets, and<Script>.capture.mdfor printing or a tablet on the stage. - From Python, for one script or a few scenes:
ss.capture_list("Ep01", scenes=["sc010", "sc020"], fmt="md"). Addonly="needs_recapture"for a pickup session, orinclude_action=Falsefor dialogue only. - Exporting only reads the sync record. It never changes a sequence or a take.
Recapture flags
- When a line that has already been captured is rewritten in the script, the next sync flags it: the report window lists it with Needs recapture and Dismiss, and the capture list shows yes under Needs recapture
- The flag clears when a new take of the line is recorded, when the line is changed back to the captured words, or when you choose Dismiss. Dismiss holds only for those exact words: rewrite the line again and it’s flagged again. Needs recapture keeps it listed on every sync
- Only script text is compared. A line performed differently on stage isn’t detected
- Nothing in Sequencer changes. Companion tools see the flags in the change journal
Mocap Manager
- Needs Epic’s Performance Capture Workflow plug-in (Edit > Plugins). Without it, Script Sequencer still loads and these commands explain what they need
- Slates: Tools > Capture > Send Slates to Mocap Manager adds one slate per scene or shot to the newest Mocap Manager session, named with its code, with the heading and line IDs in its note. Slates already here are kept. When a scene is cut, a slate Script Sequencer added is archived if it has no takes yet. Click Reload in Mocap Manager’s Slates panel to see them
- Takes: after recording, choose Tools > Capture > Match Mocap Takes. Every take whose slate is a code is recorded against that code’s lines, as with Take Recorder. Other slates are listed and left alone. Running it again skips takes already recorded
- Run Match Mocap Takes before rewriting lines you’ve just recorded, so a rewrite is flagged against them
- It never moves, selects or replaces a take
Series and episodes
For a series with one script per episode. Each script gets its own content folder through {episode} in the
Root Template (the title page’s Episode: value, or the script’s file name when it has none)
- Import a season: Tools > Script Sequencer > Import Script Folder… imports every
.fountainand.fdxfile in the folder, in name order (name themEp01,Ep02…Ep10so they sort correctly), with one progress window and one report. The ID-notes question is asked once for the whole folder - Nothing half-done: if two scripts would share a sync record (
Ep01.fountainandEp01.fdx) or a content folder (the sameEpisode:value), nothing is imported and the message lists each problem. Scripts already imported from the same file are left as they are, so you can run it again after adding new episodes - Batch mode (Project Settings > Plugins > Script Sequencer > Batch, off by default):
- Sync All Scripts Now gives one report with a section per episode, in the report window and the Output Log.
- Every sync record, change-journal entry and generated asset records its episode (
suite.episode), so other tools and the Content Browser can filter by episode. Assets made before you switched it on are tagged at the next sync - Scripts are checked for saves every 0.5 s up to 20 watched scripts, every second up to 60, and every 2 s beyond that
ss.status()lists scripts grouped by episode- One episode: in the Python console,
ss.sync_episode("Ep03")syncs only that episode’s scripts - From Python:
ss.import_folder(r"<folder>"), orss.import_folder(r"<folder>", pattern="*.fdx")for one format
Naming tokens
Needs Epic’s Naming Tokens plug-in (UE 5.6 and later, enabled by default). Script Sequencer adds these tokens wherever Unreal accepts naming tokens, such as Movie Render Queue output paths and Cine Assembly names:
| Token | Example |
|---|---|
{ss:unit} |
sc010, or sc010_sh0020 for a shot |
{ss:scene} |
sc010 |
{ss:shot} |
sh0020 (empty for a scene) |
{ss:scene_num} / {ss:shot_num} |
010 / 0020 |
{ss:episode} |
Ep01 |
{ss:script} |
the script’s name |
For Movie Render Queue, set Output > File Name Format to {ss:episode}/{ss:unit}/{ss:unit}.{frame_number}.
Tokens read the sequence being named, so they’re empty for a sequence Script Sequencer didn’t generate.
Final Draft (.fdx)
- Scene numbers become codes: scene
12issc012, an inserted12Abecomessc012A - Dialogue, action, character extensions such as
(V.O.), dual dialogue and Shot paragraphs are read. Parentheticals, transitions, cast lists and act breaks are skipped - Revision sets. Once revision mode is on, or any text is marked, the report and the sync record name the current set (First Revision, Second Revision, …). Each changed line is tagged with the newest set marked on it, and the report adds up the changed lines per set. A line keeps its set when you clear the marks, until its text changes again
- The file is never changed. Final Draft 13 gives every paragraph an ID of its own and keeps it when you edit the text. Script Sequencer follows each line by that ID, so a line rewritten beyond recognition keeps its scratch audio and marker, as in a Fountain script with ID notes. A pasted copy of a line is a new line
- Files without these IDs (saved by an older Final Draft version or another app) are matched by scene number and by how similar the text is, so a line rewritten beyond recognition can count as a new line. Opening and saving the script in Final Draft 13 adds the IDs. Anything you’ve taken ownership of is still kept
- If an .fdx file won’t import, open it in Final Draft and save it again, then retry. If it still fails,
export it as Fountain from a writing app that reads .fdx files (Fade In, Highland and WriterDuet), and
import the
.fountainfile instead
Naming and Folders
Names are chosen on the settings page under Structure and Naming and are fixed per script when it’s imported, so a later change to project settings never renames existing work
| Naming preset | Sequences | Shots | Cameras |
|---|---|---|---|
| Scene / shot (default) | sc010 |
sc010_sh0020 |
CAM_sc010, CAM_sc010_sh0020 |
| Sequence / shot | sq010 |
sq010_sh0010 |
CAM_sq010, CAM_sq010_sh0010 |
| Custom | Your own prefixes and templates, below |
With Custom, names come from templates with these tokens: {episode} (the title page’s
Episode: value, else the script name), {script} (the file name), {scene_num} and
{shot_num} (zero-padded numbers such as 012A, 0020), {scene} and {shot} (prefix plus
number, such as sc012A, sh0020) and, for cameras, {unit} (the sequence or shot the camera
belongs to). The Root Template (default /Game/Script/{episode}) is the folder for each
script, so each episode gets its own folder. Templates with unknown tokens, or that could give
two things the same name, are refused when you save the settings.
To import with a different scheme without touching project settings:
ss.import_script("<path>/Ep02.fountain", naming_preset="sequence_shot"). Two scripts
can’t share a folder: give each its own Episode: value, or put {script} in the Root
Template.
Each script in a project also needs its own file name, ignoring the extension: Ep01.fountain
and Ep01.fdx can’t both be imported, because the plug-in tells scripts apart by name.
Importing the second one is refused with a message, rename one of them first.
To change the structure or naming of a script that’s already imported, import it again into a new folder, existing sequences are never renamed.
The Report Window
It’s on by default: Project Settings > Plugins > Script Sequencer > Report >
Report window, or for one script ss.configure("Episode01", report_window=False)
to turn it off. After every save or Sync All Scripts Now that changes Sequencer, Script Sequencer
lists what changed, grouped by scene. A change that needs a decision has a dropdown:
| Change | Choices |
|---|---|
| A scene’s dialogue no longer fits its shot (timing on hold) | Ripple later shots · Extend the shot · Ignore |
| A scene’s dialogue now needs less time than its shot (timing on hold) | Shorten and Ripple · Keep |
| A line you edited by hand was cut from the script | Remove anyway · Keep |
| The script changed under audio or a marker you edited | Reset to script · Keep |
| A line you edited by hand is in a scene that was merged into another | Remove anyway · Keep |
- The changes are applied first. The choices you make in the window are applied as soon as you close it, by one more short sync
- An import opens the window only for warnings. With the window off, the report goes to the Output Log, and a message box appears only for new warnings or failures
- Every change shows its dropdown each time. To skip changes you’ve already answered with Ignore, Keep or Dismiss, turn on Report > Remember choices: you’re then asked again only if the scene or line changes
- Ripple and Extend only move what Script Sequencer generated. A shot you moved or trimmed, or one placed by an EDL or OTIO import, never moves
- Revisions. The window’s title names the script’s current revision, such
as Second Revision in Final Draft. In a Fountain script, add a title page
line such as
Revision: Blueto name it - Merged scenes. When you delete a scene heading so two scenes become one, the scene that brought the most lines keeps its sequence (on a tie, the earlier one). The other scene’s shot comes off the Shots track, but its Level Sequence stays, with anything you edited in it. Each line you edited there is listed: Remove anyway moves the line into the merged scene as a new scratch line, Keep leaves it where it is
Markers
Script Sequencer adds a marker at each scene heading (on the master) and at each line (in the
scene’s sequence). These settings are in Project Settings > Plugins > Script Sequencer > Markers,
and each can be set for one script too, for example ss.configure("<ScriptName>", marker_labels="full").
- Markers follow lines (on by default). Drag a line’s scratch audio in Sequencer and its marker moves to the line’s new start once you let go. A marker you moved yourself stays where you put it
- Markers follow shots (on by default). Drag a shot section on the master’s Shots track, or let an EDL or OTIO import move it, and its scene heading marker moves to the section’s new start. A marker you moved yourself stays where you put it
- Marker labels decides how much text each marker shows:
- Full text: the whole line or heading
- Shortened (rough fit to zoom) (default): each label is cut to end about where the next marker starts, at a normal working zoom, and ends in “…”. The last marker in a sequence is cut to end where the sequence’s lines end
- Character only: just the line’s speaker, or
(action)for an action line. Scene headings are shortened. Whether the ID shows as well is up to Show IDs in markers
- Show IDs in markers (off by default). The label shows only the text; its ID, such as
SCR:L-4d5e6f, stays in the marker’s comment, where Script Sequencer finds it. Turn this on to show the ID at the start of the label too. Existing project and script preferences are kept. On engines whose markers have no comment field (this may include 5.6 and 5.7), the ID stays in the label whatever this setting says, and the Output Log says so once
Changing Marker labels or Show IDs in markers updates the markers straight away, without a
sync, in every script your editor syncs. Markers on lines and shots you own are updated too. A marker
whose label you edited yourself keeps it. To update one script from the Python console, run
import script_sequencer.editor_entry as ss; ss.relabel_markers("<ScriptName>").
Sequencer draws each label to the right of its marker, but when the label would run past the right edge of the view, it draws it to the left of the marker instead, over the labels before it. That’s why a label can jump as you scroll. Shorter labels make this rarer, and zooming in avoids it.
What The Plug-in Edits
- It only changes markers, scratch audio sections, shot sections, and camera cuts it created
- Optional placeholder aiming also sets static transforms on verified, unowned generated speaker cameras
- If you move, edit, replace or delete one of those, it becomes owned by you and is never changed again. Editing any camera cut in a sequence hands that sequence’s cuts to you. The report, found in Unreal’s Output Log, tells you when the script and owned content disagree
- Names (
sc010,CAM_sc010_sh0020) are assigned once and never change. Script scene numbers (#12#,#12A#) are used when present, inserted scenes get in-between numbers when possible (sc015) - To hand an owned item back, use
import script_sequencer.editor_entry as ss;ss.reset_items("<ScriptName>", ["L-xxxxxx"]), replacing ScriptName and using the item’s ID, includingL- - Cutting a scene removes its shot section from the master but keeps its Level Sequence
- Undo doesn’t reverse a sync, because the assets, the sync record, and the change journal are saved together. To go back, revert the script and save it, or revert the assets and the sync record together in source control
Working With The Edit (EDL and OTIO)
Three things shape the master sequence, and each has one job:
- The script decides what exists: scenes, lines and shot notes
- The edit decides when and how long: where each shot section sits on the Shots track
- Ownership keeps them apart: anything someone edits is never changed again by the plug-in (unless that item is handed back)
In practice:
- Import an EDL onto the Shots track, or edit the sections by hand. Retimed and moved sections are kept exactly where the edit places them. Later script changes flow around them
- A section the edit removed is cut in edit. Its Level Sequence and everything inside it is kept. Place it back on the Shots track to bring it back
- If a Level Sequence already exists where the plug-in would create one (for example
/Game/Script/<Episode>/Sequences/sc020, made by an OpenTimelineIO import), it is adopted. It keeps the editor’s section, and only line markers and scratch audio are added inside it. Point your OTIO import hooks’unreal.sub_sequencepaths at those names - Every report has an Edit vs script list: scenes with no section on the Shots track, and sections with no scene in the script (edit-only shots). Neither is changed
Working In A Team
Several people can open the same project through source control (Perforce, Git LFS 2, or any provider set up under Revision Control in the editor’s toolbar). A single Multi-User session can also be joined. One editor per script turns its saves into Sequencer changes:
- The sync owner. The editor that imports a script owns it, recorded as
user@machinein the script’s sync record. Only the owner’s editor watches the script and syncs on save, so in a Multi-User session each change is made once and reaches everyone - Everyone else sees “synced automatically by …” in the Output Log. Sync All Scripts Now still works for them, but asks first. Take Sync Ownership moves a script to your editor, ask the current owner to submit their sync record first, so you get the latest version
- Check-out before changes. With source control on, each sync first checks out the sync record, its change journal and every asset it’s about to change, in one call. New sequences and scratch sounds are marked for add after they’re saved
- Held files wait. If someone else has the sync record checked out, or it’s out of date, or source control can’t be reached, nothing changes and the sync tells you why. If someone holds a generated sequence or sound, only the changes to it wait, the report says who holds it, and they’re applied at a later sync. Anything that person edited meanwhile is kept as theirs
- Git. With the Git LFS 2 plug-in (UEGitPlugin) and LFS locking, a lock someone else holds counts as checked out by them, and a file with newer commits on the remote is out of date: its changes wait until you pull. The sync record and journal are text files Git doesn’t lock, so the plug-in asks Git directly whether they’re behind the remote (as of your last fetch or Refresh). Unreal’s built-in Git provider has no check-outs, so there only the sync owner rule applies
ss.status()in the Python console lists each script’s owner, sync mode and whether it’s watched in your editor
Tips
- Your writing app may not show the
[[id:…]]notes the plug-in adds, or a new ID it gives a pasted line, until you close and reopen the script. TextEdit on Mac, for one, keeps showing the version it opened. The notes are in the file, reopening shows them. Close the script before saying Yes to ID notes, too, so the app doesn’t save its old copy over them - To see what the plug-in has recorded for a script — every sequence and line with its
range, what’s owned by you, what’s cut, and the last few syncs — run
import script_sequencer.editor_entry as ss; ss.inspect("<ScriptName>")in the Python console. It only reads data, so nothing changes - Live updates are faster if the editor keeps working in the background: turn off Edit > Editor Preferences > General > Performance > Use Less CPU when in Background
- Scripts edited while the editor was closed are synced about 5 seconds after startup
Data Contract, Tags and Your Own Tools
Script Sequencer writes what it generates in a documented file format, the data contract
(Docs/DataContract.md, a technical spec), so your own pipeline scripts and tools can read it.
- Every generated sequence and scratch Sound Wave carries
suite.*metadata tags: the line or scene ID, the unit code, the character, and the slot. To filter the Content Browser by them, see Content Browser filters below - The sync record and change journal (see Files) say what each sync changed, line by line
- Replacing scratch audio with your own sound (a recorded take, say) changes the line to become owned and its audio is never touched by the plug-in
Content Browser Filters
The plug-in registers the suite.* keys with the Asset Registry when the editor starts, so you can type,
for example, suite.line=L-4d5e6f in the Content Browser’s search box. If a search finds nothing, add the keys yourself: open Edit > Project Settings > Game > Asset Manager, find Asset Registry > Metadata Tags For Asset Registry, add suite.contract, suite.generator, suite.script, suite.unit, suite.scene, suite.line, suite.character, suite.slot and suite.take, and restart the editor.
Files
- Sync record:
<Project>/ScriptSequencer/<ScriptName>.sync.json, the plug-in’s record of what it generated from the script and where. Comparing it with Sequencer is how the plug-in knows what you’ve changed. Check it into source control with the generated assets. Only Script Sequencer writes it. Don’t edit it by hand - Change journal:
<Project>/ScriptSequencer/<ScriptName>.changes.jsonl, one line per sync (the last 1,000). Check it in with the sync record - Scratch audio: one silent Sound Wave of about 3 KB per dialogue line, in
/Game/Script/<Episode>/Scratch/ Docs/DataContract.md,Docs/suite_contract_v1.1.schema.jsonandDocs/suite_contract_v1.0.schema.json: the data contract (version 1.1), the technical spec of these files
Known Limitations
- Script Sequencer is built for virtual/CG productions in Unreal. Live-action workflows have limited support
- Final Draft files are never written to. Lines are followed by Final Draft’s own paragraph IDs (Final Draft 13). Files without them are matched by text. A line’s revision set is the newest one marked on its text
- Scratch audio is silence: placeholder timing, not a voice. Timing is estimated from word count unless held
- Take Recorder takes are protected as owned content. Captures record which lines a take covers. They never choose, move or replace audio
- One editor per script syncs automatically, others sync on request
- Batch mode records each script’s episode once it’s switched on. Switching it off keeps the episode already written
- Structure and naming can’t be changed after import without importing again into a new folder
- Marker labels can still overlap when you zoom far out: Sequencer draws every label in full
- Casting creates one empty character binding per scene. It never places, animates or retargets anyone
- Speaker cameras are static placeholders, aimed only at characters someone has placed. Reaction shots come from shot notes. A line shorter than Speaker Cut Min Seconds (1.5 s by default) doesn’t get its own placeholder cut
- Take hooks work with Take Recorder, including virtual-camera recordings. Takes can be recorded from any editor, not only the sync owner’s
- Mocap Manager support is a slate export, plus matching takes back to lines where supported. It depends on Epic’s Performance Capture Workflow plug-in, which may change between engine versions
- Changed-after-capture flags compare script text. A line performed differently on stage isn’t detected
- Naming tokens need Epic’s Naming Tokens plug-in (UE 5.6+). Where they can be used depends on Epic’s support for custom tokens
Support
Email studio22.development@gmail.com with your engine version, the Output Log from the sync, its .sync.json, and if you can, the relevant section of the script itself.