How-To

DGX Spark Tutorial 2: Generate Images and Moving Video with ComfyUI

Generate original reference images and short Wan clips with ComfyUI, save native prompt IDs and source ledgers, and inspect actual motion before scaling a video project.

DGX Spark Tutorial 2: Generate Images and Moving Video with ComfyUI

Part 2 of the Labverse DGX Spark Media Lab series. Series overview · Public repository

Goal: produce one original image, one text-to-video clip and optionally one image-conditioned clip, with saved settings and receipts. Complete Tutorial 1 first. These are short experimental renders, not a guaranteed camera/character simulation.

1. Download only the selected components

Review model and license notes. The manifest pins repository revisions and individual files; it does not redistribute weights. Image generation uses Z-Image-Turbo; video uses Wan 2.1 14B, not the distinct Wan 2.2 5B Diffusers bundle used in another private experiment.

export SPARK_MEDIA_ROOT="$HOME/spark-media"
python3 scripts/models.py --group image --root "$SPARK_MEDIA_ROOT"
python3 scripts/models.py --group video --root "$SPARK_MEDIA_ROOT"
# Only after reviewing each model's terms:
python3 scripts/models.py --group image --root "$SPARK_MEDIA_ROOT" --download --accept-licenses
python3 scripts/models.py --group video --root "$SPARK_MEDIA_ROOT" --download --accept-licenses

The image group is about 20.69 GB; T2V about 35.57 GB. Requesting I2V later adds its larger diffusion model and CLIP Vision encoder; shared text/VAE files are verified and reused. Leave room for Python environments, caches, input assets, decoded frames and multiple outputs. The helper checks planned missing bytes against available disk plus a safety margin, downloads sequentially, and verifies size and hash before promoting a .part file. A failed partial file is restarted on the next run; it is not a resumable byte-range downloader. Existing completed files are rehashed rather than silently overwritten. Save the download receipts locally.

An LFS SHA-256 match, a Git blob SHA-1 match, and a locally recorded digest have different evidential strengths. Do not label them all “publisher SHA-256 verified.” The voice dependency exception is explained in Tutorial 3.

2. Start native ComfyUI on loopback

After deliberately releasing the heavy text-model allocation if necessary:

"$SPARK_MEDIA_ROOT/.venv/bin/python" "$SPARK_MEDIA_ROOT/ComfyUI/main.py" \
  --listen 127.0.0.1 --port 8188 \
  --extra-model-paths-config "$SPARK_MEDIA_ROOT/extra_model_paths.yaml" \
  --input-directory "$SPARK_MEDIA_ROOT/comfy/input" \
  --output-directory "$SPARK_MEDIA_ROOT/comfy/output" \
  --user-directory "$SPARK_MEDIA_ROOT/comfy/user"

Keep this terminal open. From a second Spark terminal:

python3 scripts/comfy_job.py status

From Windows, substitute your own SSH alias, using your normal verified host key:

ssh -N -L 8188:127.0.0.1:8188 YOUR_SPARK_SSH_ALIAS

Open http://127.0.0.1:8188. No public firewall port or anonymous public API is needed. workflows/ui contains canvas graphs; workflows/api contains execution graphs. Do not send a UI graph to /prompt. The UI graphs were rebuilt against the recorded node schemas; fresh-version UI compatibility still needs inspection on your installation.

3. Prove export separately from inference

python3 scripts/comfy_job.py submit workflows/api/diagnostic-export.api.json \
  --receipts "$SPARK_MEDIA_ROOT/receipts/export" --execute

This creates four solid-color frames and writes a tiny MP4. It proves the export path only. It uses no generative model and cannot be cited as a successful AI-video test. The helper first checks /object_info, required inputs and model choices, then confirms the queue is empty before execution.

4. Generate an original reference image

Inspect workflows/api/z-image.api.json. Its prompt is an original simple robot on a desk, avoiding identifiable people or brand likenesses. Keep the first test small and fixed-seed. The sample image graph uses 1024×1024, eight steps and CFG 1.0.

python3 scripts/comfy_job.py submit workflows/api/z-image.api.json \
  --receipts "$SPARK_MEDIA_ROOT/receipts/image"             # validate only
python3 scripts/comfy_job.py submit workflows/api/z-image.api.json \
  --receipts "$SPARK_MEDIA_ROOT/receipts/image" --execute

The receipt records the submitted graph, its hash, native prompt ID and final history. Read the resulting filename from native output history; the helper does not guess which file is newest. Open the actual PNG and inspect object count, framing, hands/faces if present, unwanted text and unwanted likenesses. Technical success does not imply a usable composition.

For your own prompt, duplicate the JSON and edit only the positive text node, seed and desired settings. Preserve each accepted version rather than overwriting it. The same seed is useful for comparisons but does not guarantee bitwise reproducibility across software/hardware changes.

5. Run the first short T2V canary

python3 scripts/comfy_job.py submit workflows/api/wan-t2v.api.json \
  --receipts "$SPARK_MEDIA_ROOT/receipts/wan-t2v" --execute

The starter uses 512×320, 49 native frames, 16 fps, 30 steps and tiled VAE decoding. Its nominal clip duration is 49/16 = 3.0625 seconds. It is a bounded starting point, not a newly measured performance result. The dated research qualification used 81 frames at the same resolution/frame rate, produced 5.0625 seconds, and measured 286.62 seconds of generation. Whole GPU-handoff/reload time was longer.

Wan expects temporal lengths appropriate to its compression scheme; these starters use 4n+1 frames. Width/height and frame count must satisfy the installed nodes. Raise one parameter at a time after checking memory and output. Do not infer throughput from parameter count or package download speed.

The helper polls only its prompt ID. On timeout it retains the receipt and reports outcome unknown; it never submits a duplicate, clears the shared queue or interrupts another user's work. Resume observation with:

python3 scripts/comfy_job.py observe PROMPT_ID \
  --receipt /path/to/the/saved-receipt.json --timeout 3600

Use the real ID and receipt path printed at submission. If the API is unreachable, recover connectivity before deciding whether generation failed. A network failure is not a cancellation receipt.

6. Add image-to-video conditioning

Download the optional group:

python3 scripts/models.py --group i2v --root "$SPARK_MEDIA_ROOT"
python3 scripts/models.py --group i2v --root "$SPARK_MEDIA_ROOT" --download --accept-licenses

Copy your selected, rights-cleared reference PNG to $SPARK_MEDIA_ROOT/comfy/input/input_image.png, or change the LoadImage filename in a versioned graph. No reference image is shipped. Load workflows/ui/wan-i2v.json in the canvas to inspect the conditioning connections, or submit its API counterpart:

python3 scripts/comfy_job.py submit workflows/api/wan-i2v.api.json \
  --receipts "$SPARK_MEDIA_ROOT/receipts/wan-i2v" --execute

Specify one subject action and one camera intent per shot. For example: “The small robot raises one arm slowly; static medium camera; desk objects remain fixed.” Negative prompts can discourage artifacts but cannot enforce physics. In our later experiments, a requested push-in became a pullback and a cricket ball traveled differently from the prompt. A graph that executes cannot certify exact camera control.

For larger work, approve a consistent character reference, wardrobe, lighting and location first. Changing a reference can change identity. An approved close-up does not establish full-scene continuity or lip-sync quality.

7. Inspect motion and build a source ledger

python3 scripts/verify_media.py /path/to/generated.mp4 --require-video
ffmpeg -i /path/to/generated.mp4 -vf fps=1 /path/to/contact-%03d.png

Inspect the moving clip at full speed, not only the contact sheet: feet, occlusions, hands, facial drift, background flicker and direction. Frame uniqueness can help catch accidental repeats, but random noise can also be unique. Accept motion visually.

Keep a ledger with shot ID, prompt/graph hash, model revision, native dimensions/fps/frame count, generation seconds, native output filename/hash, selected source-frame range and editorial decision. Example: shot-plan.json. Retain failed takes as failures.

Our original 147-second montage technically decoded but was rejected because most of it animated still images. The corrected 147.2917-second experiment used 30 locally generated Wan clips and a source-frame ledger. This is why our tutorial distinguishes generated motion from a pan over a still. Upscaling a 704×416 source to 720p does not make it native 720p detail; record source and delivery resolutions separately.

8. Release memory and restore text inference

After the queue is genuinely idle, stop your own foreground ComfyUI process with Ctrl+C or use the installed application's documented unload function. Restore the exact text service paused earlier and repeat the text/tool check. Do not rely on an HTTP status alone to prove successful inference.

Checkpoint: save one reviewed reference image and one reviewed moving clip with receipts. Continue to Tutorial 3.

Read the complete series

  1. DGX Spark Tutorial 1: Set Up Local Inference and Hermes
  2. DGX Spark Tutorial 2: Generate Images and Moving Video with ComfyUI
  3. DGX Spark Tutorial 3: Local Voiceover, Audio and Film Finishing

Open the public GitHub repository · Download release v0.1.0

Building something similar?

Talk through your project with the Labverse team.

Discuss your project