Skip to content
Automation
Skill

/ltx-director

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

From plugin
comfyui-mcp
74842 skills4 agents11 commands1 MCP
Install
$ npx -y skills add artokun/comfyui-mcp --skill ltx-director --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/ltx-director

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

ltx-director.SKILL.md
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:

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
        └ comb
Read more
Ships withcomfyui-mcp

This project is no longer maintained. ComfyUI now ships official agent and MCP tooling — Comfy Agent and Comfy MCP — built and supported by the Comfy-Org team with deeper integration than a community project can match.

Get the whole plugin

Other skills on comfyui-mcp.