New: Earn 20% as an AgentAlley affiliate. Share your link and earn on every sale you refer. Join now New: Earn 20% as an affiliate. Join now

Drive the LTX Director node

LTX Director (Timeline)

by artokun · MIT license · ★ 759 on GitHub · updated Sep 21

LTX Director (Timeline) cover

Drive the LTX Director (Timeline) node. Its Add Image/Text/Audio buttons are DOM-only and cannot be clicked by an agent; edit the hidden timeline_data JSON widget instead.

---
name: ltx-director
description: Drive the LTX Director (Timeline) node. Its Add Image/Text/Audio buttons are DOM-only and cannot be clicked by an agent; edit the hidden timeline_data JSON widget instead. Load when a workflow contains LTXDirector / LTXDirectorGuide / PromptRelayEncodeTimeline, or when asked to add, move, retime, or remove timeline segments (image / text / audio / motion).
---

# LTX Director (Timeline)

`LTXDirector` (pack: **WhatDreamsCost-ComfyUI**, category `WhatDreamsCost`) is a
video *timeline editor* node. Its on-canvas UI has **Add Image**, **Add Text**
and **Add Audio** buttons.

## You cannot click those buttons. Don't try.

They are not node inputs. The pack builds them as raw DOM elements with JS
handlers:

```js
addTextBtn.addEventListener("click", () => this.addTextSegmentFreeSpace());

Overview

LTXDirector (pack: WhatDreamsCost-ComfyUI, category WhatDreamsCost) is a video timeline editor node. Its on-canvas UI has Add Image, Add Text and Add Audio buttons.

Manual install (npx, ZIP)
Claude Codenpx skills add artokun/comfyui-mcp --skill ltx-director -g -a claude-code
Claude Code (manual)mkdir -p ~/.claude/skills && curl -sL https://codeload.github.com/artokun/comfyui-mcp/tar.gz/1d5b12b1b499 \ | tar -xz -C ~/.claude/skills --strip-components=3 comfyui-mcp-1d5b12b1b499/plugin/skills/ltx-director
Windows (PowerShell)New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Invoke-WebRequest "https://m.agentalley.io/zip/74e24cff43520ca9e0d8/ltx-director.zip" -OutFile "$env:TEMP\ltx-director.zip"; Expand-Archive "$env:TEMP\ltx-director.zip" -DestinationPath "$HOME\.claude\skills" -Force
Claude.ai / desktopDownload the ZIP and upload it under Settings → Capabilities → Skills.
Codex, Cursor, other agentsPoint them at SKILL.md. The other files are plain text.

Connects with Agent Alley MCP · agentalley.io/mcp

Files included

  • SKILL.md7.0 KB
Read the files here
artokun/1d5b12b1 file
SKILL.md
Show the full text183 lines

LTX Director (Timeline)

LTXDirector (pack: WhatDreamsCost-ComfyUI, category WhatDreamsCost) is a video timeline editor node. Its on-canvas UI has Add Image, Add Text and Add Audio buttons.

You cannot click those buttons. Don't try.

They are not node inputs. The pack builds them as raw DOM elements with JS handlers:

addTextBtn.addEventListener("click", () => this.addTextSegmentFreeSpace());

Panel/MCP tools drive the LiteGraph node model (widgets + inputs). They cannot invoke arbitrary DOM handlers, so "click Add Text" is impossible. This is a hard limitation, not flakiness. Say so plainly rather than retrying.

The real control surface: timeline_data

Everything those buttons do is serialized into ONE string widget, timeline_data. The pack's own code treats it as "the absolute source of truth". Set it and the editor renders it.

It is listed in the pack's HIDDEN_WIDGET_NAMES, so it does not appear in the node's visible widget list, but it is an ordinary input and is settable:

create_workflow(action="modify", workflow=<the graph>, operations=[
  { op: "set_input", node_id: "42",
    input_name: "timeline_data", value: "<json string>" },
])

The value is a JSON string, not an object. Empty state is "{}".

Sibling hidden widgets: local_prompts, segment_lengths, guide_strength, audio_data, use_custom_audio, inpaint_audio, use_custom_motion, override_audio.

Schema (verified against a real production workflow)

{
  // TRACK GATES — a track's segments are IGNORED unless its gate is true.
  "mainTrackEnabled": true,
  "audioTrackEnabled": false,
  "motionTrackEnabled": false,

  // must mirror the node's start_frame / duration_frames widgets (see Edges)
  "normalStartFrame": 0,
  "normalDurationFrames": 4393,

  "global_prompt": "",
  "overrideAudio": false,
  "inpaint_audio": false,

  // retake mode
  "retakeMode": false, "retakeStart": 24, "retakeLength": 48,
  "retakePrompt": "", "retakeStrength": 1, "retakeVideo": null,
  "retake_global_prompt": "",

  // display-only
  "propHeight": 90, "globalPropHeight": 60, "showFilenames": true,

  "segments": [],        // main track: image AND text segments
  "motionSegments": [],
  "audioSegments": []
}
Text segment (segments)
{ "id": "seg1", "start": 0, "length": 48, "prompt": "wide shot, neon city", "type": "text" }
Image segment (segments)
{
  "id": "1784657553220x4fxu",
  "start": 0,
  "length": 241.68,
  "prompt": "",
  "type": "image",
  "imageFile": "whatdreamscost/Nintendo_ZeldaUrbosaHotspringKF_00002_.png",
  "imageB64": "/api/view?filename=Nintendo_ZeldaUrbosaHotspringKF_00002_.png&type=input&subfolder=whatdreamscost"
}

imageB64 is a misnomer. It holds a /api/view URL, not base64. So an image segment points at a file already in ComfyUI's input dir. Full agent-drivable recipe:

  1. upload_image (action:"image") → puts the file in the input dir (note its subfolder/name)
  2. imageFile = "<subfolder>/<name>.png"
  3. imageB64 = "/api/view?filename=<name>.png&type=input&subfolder=<subfolder>"
Audio segment (audioSegments)
{
  "id": "17846562234967gjvk", "type": "audio",
  "start": 0, "length": 4393, "trimStart": 0, "audioDurationFrames": 4393,
  "audioFile": "whatdreamscost/Intergalactic Hip Hop.mp3",
  "fileName": "Intergalactic Hip Hop.mp3",
  "waveformPeaks": [0.047, 0.274, 0.537, "…~200 floats…"]
}

waveformPeaks is the rendered waveform. It is cosmetic. Omit it or pass [] if you're writing a segment programmatically; the audio still plays. Don't fabricate plausible-looking peaks and imply they were measured.

Edges (get these wrong and it silently does nothing)

  1. Track gates. Pushing into audioSegments does nothing while audioTrackEnabled is false. Set the gate in the same edit. (The reference workflow ships an audio segment with the track OFF, which is easy to misread as broken.)
  2. Frame bookkeeping must agree. normalDurationFrames == duration_frames widget == segment_lengths widget (observed: all 4393), and normalStartFrame == start_frame. Change the timeline length and you must update all of them.
  3. Frames, not seconds. start / length are pixel-space frames and may be fractional (241.68). duration_seconds / frame_rate / time_units are display concerns.
  4. global_prompt exists twice, inside timeline_data and as a forceInput socket on the node. In the reference workflow the socket is wired from a PrimitiveStringMultiline; prefer the wired source and keep them consistent.
  5. guide_data must go somewhere. LTXDirector.guide_data → LTXDirectorGuide. That partner node is what injects the keyframes.
  6. Unsupported by the pack. The widget tooltip says "auto-managed; do not edit by hand". Well-formed blobs load fine; malformed JSON will break the editor. Round-trip and re-read after writing, and tell the user this is unofficial.

Reference wiring (from a working LTX 2.3 Director graph)

Power Lora Loader (rgthree) ─ model ─┐
DualCLIPLoader ──────────── clip ────┤
VAELoaderKJ ───────────── audio_vae ─┤   LTXDirector #42
PrimitiveStringMultiline ─ global_prompt ─┘
        │ model         → LTX2_NAG
        │ positive      → ConditioningZeroOut, LTXVConditioning
        │ audio_latent  → LTXVConcatAVLatent
        │ guide_data    → LTXDirectorGuide   ← required partner
        │ frame_rate    → AudioToFrameCount, VHS_VideoCombine
        └ combined_audio→ AudioToFrameCount, PreviewAudio, …

Procedure

  1. Find the node: get_workflow (action:"query") with types: ["LTXDirector"], fields: "detail" (or panel_query_graph on the live canvas).
  2. Read the current timeline_data and JSON.parse it. Never hand-splice the string.
  3. Mutate the parsed object (append a segment, flip a track gate, retime).
  4. Keep normalStartFrame / normalDurationFrames in sync with the widgets (edge 2).
  5. Write it back as a string via set_input.
  6. Re-read to confirm it round-trips, then create_workflow (action:"validate").

PromptRelayEncodeTimeline (pack: ComfyUI-PromptRelay) carries the same timeline_data hidden-widget pattern, so this skill's approach applies there too.

Any node whose controls are DOM widgets over hidden state is invisible to clicking and must be driven through its state widget. LTX Director is the clearest example.

Sources

  • Official: none found.
  • Empirical: timeline_data schema and edge rules from a working LTX 2.3 Director graph.
1---
2name: ltx-director
3description: Drive the LTX Director (Timeline) node. Its Add Image/Text/Audio buttons are DOM-only and cannot be clicked by an agent; edit the hidden timeline_data JSON widget instead. Load when a workflow contains LTXDirector / LTXDirectorGuide / PromptRelayEncodeTimeline, or when asked to add, move, retime, or remove timeline segments (image / text / audio / motion).
4---
5 
6# LTX Director (Timeline)
7 
8`LTXDirector` (pack: **WhatDreamsCost-ComfyUI**, category `WhatDreamsCost`) is a
9video *timeline editor* node. Its on-canvas UI has **Add Image**, **Add Text**
10and **Add Audio** buttons.
11 
12## You cannot click those buttons. Don't try.
13 
14They are not node inputs. The pack builds them as raw DOM elements with JS
15handlers:
16 
17```js
18addTextBtn.addEventListener("click", () => this.addTextSegmentFreeSpace());
19```
20 
21Panel/MCP tools drive the LiteGraph **node model** (widgets + inputs). They
22cannot invoke arbitrary DOM handlers, so "click Add Text" is impossible. This
23is a hard limitation, not flakiness. Say so plainly rather than retrying.
24 
25## The real control surface: `timeline_data`
26 
27Everything those buttons do is serialized into ONE string widget, `timeline_data`.
28The pack's own code treats it as *"the absolute source of truth"*. Set it and the
29editor renders it.
30 
31It is listed in the pack's `HIDDEN_WIDGET_NAMES`, so it does **not** appear in the
32node's visible widget list, but it is an ordinary input and is settable:
33 
34```
35create_workflow(action="modify", workflow=<the graph>, operations=[
36 { op: "set_input", node_id: "42",
37 input_name: "timeline_data", value: "<json string>" },
38])
39```
40 
41The value is a JSON **string**, not an object. Empty state is `"{}"`.
42 
43Sibling hidden widgets: `local_prompts`, `segment_lengths`, `guide_strength`,
44`audio_data`, `use_custom_audio`, `inpaint_audio`, `use_custom_motion`,
45`override_audio`.
46 
47## Schema (verified against a real production workflow)
48 
49```jsonc
50{
51 // TRACK GATES — a track's segments are IGNORED unless its gate is true.
52 "mainTrackEnabled": true,
53 "audioTrackEnabled": false,
54 "motionTrackEnabled": false,
55 
56 // must mirror the node's start_frame / duration_frames widgets (see Edges)
57 "normalStartFrame": 0,
58 "normalDurationFrames": 4393,
59 
60 "global_prompt": "",
61 "overrideAudio": false,
62 "inpaint_audio": false,
63 
64 // retake mode
65 "retakeMode": false, "retakeStart": 24, "retakeLength": 48,
66 "retakePrompt": "", "retakeStrength": 1, "retakeVideo": null,
67 "retake_global_prompt": "",
68 
69 // display-only
70 "propHeight": 90, "globalPropHeight": 60, "showFilenames": true,
71 
72 "segments": [], // main track: image AND text segments
73 "motionSegments": [],
74 "audioSegments": []
75}
76```
77 
78### Text segment (`segments`)
79 
80```json
81{ "id": "seg1", "start": 0, "length": 48, "prompt": "wide shot, neon city", "type": "text" }
82```
83 
84### Image segment (`segments`)
85 
86```json
87{
88 "id": "1784657553220x4fxu",
89 "start": 0,
90 "length": 241.68,
91 "prompt": "",
92 "type": "image",
93 "imageFile": "whatdreamscost/Nintendo_ZeldaUrbosaHotspringKF_00002_.png",
94 "imageB64": "/api/view?filename=Nintendo_ZeldaUrbosaHotspringKF_00002_.png&type=input&subfolder=whatdreamscost"
95}
96```
97 
98**`imageB64` is a misnomer. It holds a `/api/view` URL, not base64.** So an
99image segment points at a file already in ComfyUI's **input** dir. Full
100agent-drivable recipe:
101 
1021. `upload_image (action:"image")` → puts the file in the input dir (note its `subfolder`/name)
1032. `imageFile` = `"<subfolder>/<name>.png"`
1043. `imageB64` = `"/api/view?filename=<name>.png&type=input&subfolder=<subfolder>"`
105 
106### Audio segment (`audioSegments`)
107 
108```json
109{
110 "id": "17846562234967gjvk", "type": "audio",
111 "start": 0, "length": 4393, "trimStart": 0, "audioDurationFrames": 4393,
112 "audioFile": "whatdreamscost/Intergalactic Hip Hop.mp3",
113 "fileName": "Intergalactic Hip Hop.mp3",
114 "waveformPeaks": [0.047, 0.274, 0.537, "…~200 floats…"]
115}
116```
117 
118`waveformPeaks` is the rendered waveform. It is cosmetic. Omit it or pass `[]` if
119you're writing a segment programmatically; the audio still plays. Don't fabricate
120plausible-looking peaks and imply they were measured.
121 
122## Edges (get these wrong and it silently does nothing)
123 
1241. **Track gates.** Pushing into `audioSegments` does nothing while
125 `audioTrackEnabled` is `false`. Set the gate in the same edit. (The reference
126 workflow ships an audio segment with the track OFF, which is easy to misread
127 as broken.)
1282. **Frame bookkeeping must agree.** `normalDurationFrames` ==
129 `duration_frames` widget == `segment_lengths` widget (observed: all `4393`),
130 and `normalStartFrame` == `start_frame`. Change the timeline length and you
131 must update all of them.
1323. **Frames, not seconds.** `start` / `length` are pixel-space frames and may be
133 fractional (`241.68`). `duration_seconds` / `frame_rate` / `time_units` are
134 display concerns.
1354. **`global_prompt` exists twice**, inside `timeline_data` and as a `forceInput`
136 socket on the node. In the reference workflow the socket is wired from a
137 `PrimitiveStringMultiline`; prefer the wired source and keep them consistent.
1385. **`guide_data` must go somewhere.** `LTXDirector.guide_data` →
139 `LTXDirectorGuide`. That partner node is what injects the keyframes.
1406. **Unsupported by the pack.** The widget tooltip says *"auto-managed; do not
141 edit by hand"*. Well-formed blobs load fine; malformed JSON will break the
142 editor. Round-trip and re-read after writing, and tell the user this is
143 unofficial.
144 
145## Reference wiring (from a working LTX 2.3 Director graph)
146 
147```
148Power Lora Loader (rgthree) ─ model ─┐
149DualCLIPLoader ──────────── clip ────┤
150VAELoaderKJ ───────────── audio_vae ─┤ LTXDirector #42
151PrimitiveStringMultiline ─ global_prompt ─┘
152 │ model → LTX2_NAG
153 │ positive → ConditioningZeroOut, LTXVConditioning
154 │ audio_latent → LTXVConcatAVLatent
155 │ guide_data → LTXDirectorGuide ← required partner
156 │ frame_rate → AudioToFrameCount, VHS_VideoCombine
157 └ combined_audio→ AudioToFrameCount, PreviewAudio, …
158```
159 
160## Procedure
161 
1621. Find the node: `get_workflow (action:"query")` with `types: ["LTXDirector"]`, `fields: "detail"`
163 (or `panel_query_graph` on the live canvas).
1642. Read the current `timeline_data` and `JSON.parse` it. **Never** hand-splice the string.
1653. Mutate the parsed object (append a segment, flip a track gate, retime).
1664. Keep `normalStartFrame` / `normalDurationFrames` in sync with the widgets (edge 2).
1675. Write it back as a string via `set_input`.
1686. Re-read to confirm it round-trips, then `create_workflow (action:"validate")`.
169 
170## Related
171 
172`PromptRelayEncodeTimeline` (pack: ComfyUI-PromptRelay) carries the **same**
173`timeline_data` hidden-widget pattern, so this skill's approach applies there too.
174 
175Any node whose controls are DOM widgets over hidden state is invisible to
176clicking and must be driven through its state widget. LTX Director is the
177clearest example.
178 
179## Sources
180 
181- **Official:** none found.
182- **Empirical:** timeline_data schema and edge rules from a working LTX 2.3 Director graph.
183 
View all files on GitHub →

Details

Updated
Sep 21, 2026
License
MIT
Stars on the repo
★ 759
Checked
Source
artokun/comfyui-mcp ↗
Creator
artokun · 44 listings
View creator

Similar skills

See all →
SkillMake a polished video of an app or a featureMake a demo video with OpenScreengetopenscreen/openscreen · by getopenscreenMake a polished video of an app or a feature with OpenScreen's MCP tools, from storyboard to recording, editing and export, then either attach it to a pull request or draft a post on X. Use when the user asks for a demo, a demo video, a screen recording, a product video, a launch clip, a video for X/Twitter, LinkedIn or a README, or to show a change in a pull request ("add a video to the PR", "show the feature to reviewers"). Also use to set OpenScreen up for an agent, and to continue an OpenScreen demo in a new session.★ 4.5k GitHub starsSkillVideo and audio processing with FFmpegFFmpeg for Video Productiondigitalsamba/claude-code-video-toolkit · by digitalsambaVideo and audio processing with FFmpeg. Use for format conversion, resizing, compression, audio extraction, and preparing assets for Remotion. Triggers include converting GIF to MP4, resizing video, extracting audio, compressing files, or any media transformation task.★ 2.1k GitHub starsSkillTurn static SCREENSHOTS into a simulated screen recordingfake-screencast — screenshots → simulated screen recordinghassancs91/claude-youtube-editor · by hassancs91Turn static SCREENSHOTS into a simulated screen recording (TSX) — a fake screencast with an animated cursor that eases to targets and clicks, a browser URL bar that updates per page, hard-cut navigations, in-page filter crossfades, smooth scroll, and a ken-burns zoom onto the payoff. Use when a beat needs to show a walkthrough of a web app / dashboard / settings page and you'd rather build it from screenshots than get (or ask for) a real screen recording — "fake a screen recording", "turn these screenshots into a screencast", "simulate clicking through the dashboard", "animate this UI walkthrough", "cursor moving through the app". Built on remotion/src/lib/screencast.tsx. This is a technique WITHIN step 2 (make-tsx): defer timeline/render/bake orchestration to make-tsx and raw crash-free TSX rules to vidtsx-2d-generator. Not for a single static page clone (use WebBrowserFrame directly) and not when the point is genuine proof of real speed/output (use a real recording).★ 330 GitHub starsSkillA Khan Academy style explainer videoKhan explainerhaiderfarooq3/khan-explainer · by haiderfarooq3Use when the user wants a Khan Academy style explainer video, a blackboard or whiteboard lesson, a hand-drawn chalk-talk animation, a short narrated video that teaches a concept ("explain X in a video", "explainer video", "teach X in 30 seconds"), or a tutorial that draws hand-written arrows, circles and captions over product screenshots ("draw over the UI", "annotated walkthrough"), or clips cut from a YouTube video or podcast where the speaker's real audio plays under a blackboard drawn to it ("khanify this video", "clip this with the Khan visual"), especially when it must be free, local, and use no paid APIs or keys. macOS only.★ 113 GitHub starsSkillPick the output format that makes an explanation easiestUnderstanding ladderChangWenC/understanding-ladder · by ChangWenCPick the output format that makes an explanation easiest to understand, following Karpathy's understanding ladder — (1) plain controlled text, (2) diagram, (3) interactive HTML page, (4) 3Blue1Brown-style explainer video. Classify the content (definition/steps, process/causality, parameter→outcome, unfolding derivation), recommend the lowest rung that fully answers, then produce it. Works in English and Chinese. Use when the user says "explain this clearly", "help me understand", "draw a diagram", "make it interactive", "make an explainer video", "what's the best way to explain X", "understanding ladder", "Karpathy ladder", or 「讲清楚」「帮我理解」「画个图」「做个交互页」「做个讲解视频」「用哪种方式讲最好懂」「理解阶梯」; also when explaining a non-trivial concept, mechanism, algorithm, or an AI agent's output.★ 108 GitHub starsSkillAnimate first frames into A-roll talking head clipsAnimate (A-Roll)TheMattBerman/scrollclaw · by TheMattBermanAnimate first frames into A-roll talking head clips. Uses Sora 2 via fal.ai (primary) with Kling 3 auto-fallback when Sora is unavailable or sunset.★ 71 GitHub stars

More from artokun

SkillTrain custom LoRAs with ostris AI-ToolkitAI-Toolkit LoRA Trainer (WAN 2.2 & Z-Image)artokun/comfyui-mcp · by artokunTrain custom LoRAs with ostris AI-Toolkit. Covers WAN 2.2/2.1 (people, styles, video motion) and Z-Image (Turbo & Base, low-VRAM image LoRAs). Use when the user wants to train a WAN or Z-Image LoRA; covers local + RunPod setup, dataset prep, key params, and using the result in a ComfyUI workflow.★ 759 GitHub starsSkillAnime/illustration text-to-imageANIMA 1.0 (Anima Base Ultra) Text-to-Image Workflowsartokun/comfyui-mcp · by artokunAnime/illustration text-to-image (ANIMA 1.0, ~2B Cosmos DiT). Use for anime, manga, illustrated characters; accepts Danbooru tags + natural language; runs/trains on <6GB VRAM; includes anime inpainting via Anima-LLLite ControlNet★ 759 GitHub starsSkillAuthoring & Publishing ComfyUI Custom Nodesartokun/comfyui-mcp · by artokunAuthoring & publishing ComfyUI custom nodes to the Comfy Registry, covering node structure, pyproject.toml spec, comfy-cli publishing, and CI★ 759 GitHub starsSkillTrain a custom anime LoRA on the ANIMA base modelCitron Anima LoRA Trainerartokun/comfyui-mcp · by artokunTrain a custom anime LoRA on the ANIMA base model with Citron's local Gradio trainer (kohya sd-scripts), <6GB VRAM, character/style LoRAs; covers setup, dataset prep, training params, and using the result in the anima-base workflow★ 759 GitHub starsSkillDiscover Civitai models with the BUILT-IN downloadmodelCivitai + comfyui-mcpartokun/comfyui-mcp · by artokunDiscover Civitai models with the BUILT-IN download_model action:"search_civitai" and install/generate them locally. Find a checkpoint/LoRA/embedding on Civitai, download it into ComfyUI, and use its trigger words. Optionally pair the official Civitai MCP for community features (images browsing, posting, collections).★ 759 GitHub starsSkillDiagnose and fix video/image color OBJECTIVELYColor Correction (measure, don't eyeball)artokun/comfyui-mcp · by artokunDiagnose and fix video/image color OBJECTIVELY with the get_image (action:"analyze_color") tool (scopes/stats such as black/white points, contrast, saturation, clipping, cast) instead of eyeballing a contact sheet. Covers the "washed out" signature, why reference color-match (mkl/ColorMatch/ColorMatchAdobe) CAN'T add contrast a flat source lacks, the levels/contrast-stretch fix (core AdjustContrast / CurveEditor), the measure→fix→re-measure loop, the side-by-side sandbox pattern, and where to place the fix in a render graph (after decode, before save). Use when a render looks washed out / flat / dull / over-saturated / color-cast, or when deciding between a color-match and a contrast/levels fix.★ 759 GitHub stars