Agent guide¶
This page is for an LLM (or an LLM-driven coding agent) generating VisualKit code, whether that's a one-off script or a long-running process that assembles videos from instructions at runtime. It assumes you've read 02-concepts.md; it doesn't repeat the model, it tells you how to move through it without guessing.
Decision tree: what am I building?¶
- Do I have a real media file (video/image) already? →
MediaClip. - Is it text I want rendered as a clip, with its own styling/animation?
→
TextClip. (It's a fullVisualClip— transform, keyframes, animation presets, everything in 06-keyframes-and-effects.md applies.) - Is it audio only? →
AudioClip. - Do I need to generate the visual itself (an infographic, a chart, a
branded card, anything drawn rather than filmed)? →
CodedVisualClip. Read 05-coded-visuals.md fully before writing the HTML — the design-canvas and variable-escaping rules are not optional details. - Am I building something reusable — a scene with a few things that
change per use (a name, a color, a headline) and a lot that stays fixed?
→ build it once as a
CompoundClipwithexpose_parameter(...), then drive it withset_parameter(...)per instance. See 04-templates-and-compounds.md. Don't regenerate the whole inner structure from scratch every time you need a slightly different instance of the same scene — that's exactly whatCompoundClipexists to avoid. - Do I need this to land in DaVinci Resolve rather than as a
standalone file? Check 07-exporting.md's feature
table before reaching for keyframes/chroma key/masks/animation —
Resolve export silently drops all of them and renders the plain static
transforminstead. If you need those effects and the target is Resolve, they aren't representable in that export path today; don't generate code that assumes otherwise.
Discover before you assume¶
If you're operating on a CompoundClip or CodedVisualClip you didn't
just build yourself in this same script (loaded from a file, fetched from
a library, handed to you by a user), read its schema, don't guess its
fields:
# A compound template
for p in compound.exposed_parameters:
... # p.name, p.label, p.description, p.required, p.default
# A coded visual bundle
clip.load_manifest()
clip.variables # {name: Variable}
clip.get_missing_required_variables()
Both ExposedParameter and Variable are Pydantic models with
label/description/required/default — that metadata exists
specifically so you (or the human you're assisting) never have to open the
inner timeline's Python or the bundle's HTML to know what a template needs.
Recipes¶
Fill a template and place it on a timeline:
lower_third.set_parameter("speaker_name", "Dr. Jane Doe")
lower_third.timeline_start = vk.Time.from_seconds(3)
root_timeline.add_clip(lower_third) # audio companion is automatic
Generate several instances of the same template cheaply: build the
CompoundClip once, then for each instance, copy it (or rebuild the light
wrapper — the inner timeline is the expensive part to construct, not the
set_parameter calls) and vary only the exposed parameters. Don't
re-author the inner timeline per instance.
Check what a batch of edits will do before doing it: Timeline.add_clip
accepts several clips in one call and the whole batch is atomic — if you're
generating N clips programmatically, prefer one batched add_clip call
over N separate calls so a bad clip in the middle can't leave the timeline
half-edited.
Preview before a full export: clip.preview_image() /
clip.preview_frame(time) (coded visuals), MediaClip.preview_image(),
TextClip.preview_image() all render a single frame to a PNG without
touching the rest of the timeline or running a full FFmpeg export — use
these for a fast feedback loop while iterating, then export once you're
confident.
Serialize/inspect a timeline as data: timeline.model_dump_json() /
Timeline.model_validate_json(...). If you're reasoning about a timeline
programmatically (checking for overlaps, summarizing what's on it, diffing
two versions), work with the parsed model, not string-matched Python
source.
Pre-flight checklist before you run an export¶
- [ ] Every clip
idis unique (duplicates raise atadd_cliptime, but check your own generation logic rather than relying on the exception as the first signal). - [ ] For a coded visual:
clip.lint()andclip.validate_bundle()come back clean before you spend a Chrome render on it. - [ ] For a template:
get_missing_required_parameters()/get_missing_required_variables()are empty. - [ ] You've picked the exporter that actually supports the effects you used (see the table in 07-exporting.md).
- [ ] If the export can fail (FFmpeg not installed, Chrome not found,
source file missing), you're catching the specific
VisualKitErrorsubclass that tells you what to fix — see 08-errors-and-validation.md — not a bareexcept Exception.
Footguns specific to generated code¶
- Don't hand-roll
Timefrom a float and skip the constructor. UseTime.from_seconds(...)/Time.from_frames(...)so the exact-fraction guarantees actually hold. - Don't assume an unknown keyword argument gets ignored. Every model
is
extra="forbid"— a field name you invented because it seemed plausible will raiseValidationError, not silently vanish. Check 03-clips-reference.md for the real field name. - Don't put an arbitrary (untyped) string variable straight into a
coded visual's
<style>block via{{ }}— it's escaped for HTML but that doesn't make it safe CSS. Validatedcolor/numbervariables are fine; free-formstringvariables aren't. - Don't assume Resolve export preserves keyframes/effects. It doesn't,
today. Re-read the table in 07-exporting.md if you're
about to generate a
keyframes=/chroma_key=/mask=/animation=argument on a clip headed forexport_to_resolve(). - Don't compose an animated child under a non-identity compound
transform. It raises
NotImplementedErroron flatten by design (see 04-templates-and-compounds.md) rather than silently rendering wrong — treat that error as "give the child its final position directly," not as a bug to retry around.