How I Structure a Reproducible Local AI Video Workflow with ComfyUI
Generating one AI video is easy. Reproducing the same experiment two weeks later is surprisingly difficult.
I ran into this problem while testing local video-generation workflows. After several iterations, my output folder looked something like this:
output_01.mp4
output_02.mp4
output_final.mp4
output_final_2.mp4
output_really_final.mp4
The videos were there, but the useful context was gone.
Which prompt produced output_02.mp4?
Which seed did I use?
Did I change the resolution?
Was this generated with the same model checkpoint as the previous test?
I eventually stopped treating generated videos as standalone files and started treating each generation as a reproducible experiment.
This article describes the simple structure I now use.
The problem: an MP4 is not enough
A generated video is really the result of several inputs:
Model
+ Prompt
+ Seed
+ Resolution
+ Frame count
+ Sampling parameters
+ Workflow version
= Output
If we save only the output, most of the experiment disappears.
For casual generation this may not matter. But it becomes a problem when we want to:
- compare model versions;
- reproduce a successful shot;
- change only one parameter;
- share an experiment with another developer;
- debug an unexpected result.
So I wanted every generated video to have a small manifest describing exactly how it was created.
Project structure
I started with a simple directory layout:
video-experiments/
├── workflows/
│ ├── text-to-video-v1.json
│ └── text-to-video-v2.json
│
├── prompts/
│ └── product-shot.txt
│
├── runs/
│ ├── 2026-08-001/
│ │ ├── manifest.json
│ │ └── output.mp4
│ │
│ └── 2026-08-002/
│ ├── manifest.json
│ └── output.mp4
│
└── scripts/
└── create-run.js
The important part is manifest.json.
Instead of trying to encode everything into the filename, I keep the metadata next to the generated asset.
Defining the generation manifest
Here is a simplified example:
{
"runId": "2026-08-001",
"model": "video-model",
"workflow": "text-to-video-v2.json",
"promptFile": "product-shot.txt",
"seed": 482913,
"width": 1280,
"height": 720,
"frames": 121,
"fps": 24,
"createdAt": "2026-08-18T10:30:00Z",
"status": "generated"
}
This gives me enough information to answer the most common question:
What exactly changed between these two videos?
For example, if I want to test a different seed, I duplicate the configuration and change only:
{
"seed": 918245
}
Everything else stays fixed.
That makes the comparison much more useful.
Creating experiment folders automatically
Creating these folders manually becomes annoying, so I use a small Node.js script.
const fs = require("fs");
const path = require("path");
function createRun(config) {
const runDir = path.join(
__dirname,
"..",
"runs",
config.runId
);
fs.mkdirSync(runDir, { recursive: true });
const manifestPath = path.join(
runDir,
"manifest.json"
);
fs.writeFileSync(
manifestPath,
JSON.stringify(config, null, 2)
);
console.log(`Created run: ${config.runId}`);
}
createRun({
runId: "2026-08-003",
model: "video-model",
workflow: "text-to-video-v2.json",
promptFile: "product-shot.txt",
seed: 482913,
width: 1280,
height: 720,
frames: 121,
fps: 24,
createdAt: new Date().toISOString(),
status: "pending"
});
Running:
node scripts/create-run.js
creates:
runs/
└── 2026-08-003/
└── manifest.json
The generated video can then be saved into the same directory.
Keeping prompts outside the workflow
Another change that helped was separating prompts from workflow files.
Instead of copying a long prompt into different JSON files, I store it as plain text:
prompts/product-shot.txt
Example:
A minimal product shot on a dark studio surface.
Slow camera push-in.
Soft directional lighting.
Subtle reflections.
The product remains centered throughout the shot.
This makes prompts easy to version with Git.
git diff prompts/product-shot.txt
Now I can see exactly what changed between two experiments.
That is much better than trying to remember which sentence I edited inside a large workflow JSON file.
Using ComfyUI as the execution layer
For local experiments, ComfyUI works well as the execution layer because workflows can be represented as graphs and saved for reuse.
My simplified pipeline looks like this:
Prompt file
↓
Experiment config
↓
ComfyUI workflow
↓
Video model
↓
Generated video
↓
Run directory
The specific model is replaceable.
For example, while exploring open-weight video workflows, I came across LTX 2.5 as one model that can fit this kind of local experimentation.
But I deliberately avoid coupling the project structure to a specific model.
Today the manifest might contain:
{
"model": "model-a"
}
Tomorrow it could be:
{
"model": "model-b"
}
The surrounding experiment structure remains unchanged.
That separation turned out to be important.
Comparing runs instead of watching them randomly
Once every generation has metadata, comparisons become easier.
Suppose I have:
Run A
seed: 100
frames: 121
Run B
seed: 200
frames: 121
I know that seed is the only variable.
Later I might compare:
Run C
seed: 100
frames: 121
Run D
seed: 100
frames: 161
Now frame count is the variable.
This sounds obvious, but without recording configuration it is very easy to accidentally change three parameters and then attribute the improvement to the wrong one.
Add notes after generation
I also added a small review section to the manifest:
{
"review": {
"motion": 4,
"promptAccuracy": 3,
"continuity": 4,
"notes": "Camera motion is good, but the object changes near the final frames."
}
}
The scores aren't meant to be scientific.
Their purpose is simply to make old experiments searchable.
If I generate 50 clips, I don't want to watch all 50 again just to remember which ones had stable motion.
What I learned
The biggest improvement to my local AI video workflow didn't come from changing a prompt.
It came from treating generation like an engineering experiment.
Instead of:
prompt → video
I now think about it as:
configuration
↓
workflow
↓
generation
↓
artifact + metadata
↓
review
↓
next experiment
This makes changing models much less disruptive because the model is only one component of the system.
It also makes failed generations useful. Even when a video looks bad, I still know exactly what produced it and can avoid repeating the same experiment.
If you're using ComfyUI or another local generation pipeline, I'd recommend starting with something very simple: save the prompt, seed, model identifier, workflow version, and output together.
You don't need a database or a complicated MLOps system.
A folder and a JSON file already solve most of the problem.
All rights reserved