Nodes/ComfyUI-UniMate/UniMate Mesh Sampler
ComfyUI Node

UniMate Mesh Sampler

How UniMate Mesh Sampler gets 2 seconds, then 7

By visualbruno·Created 21 days ago·Updated 21 days ago· 5
UniMate Mesh Sampler
  • model
  • asset
  • motion
  • info
◄promptAn object walks forward.►
◄cfg_scale3.0►
◄num_repetitions1►
◄seed0►
◄chain_overlap10►

This is the node you'll actually sit in front of. Everything before it is setup; UniMate Mesh Sampler is where you type a sentence and get motion. It's also the node with two genuinely non-obvious behaviours - the prompt format and the chaining - that decide whether you find this pack delightful or useless.

Quick context, because it reframes the whole thing: text-to-motion is not a solved genre, and the models that exist are almost all humanoid. Tencent's HY-Motion is a billion-parameter text-to-motion model and it does people. UniMate's pitch is different - any rigged skeleton. A spider, a horse, a bird, a dragon, someone's weird hand-made rig. So when the results look odd on your creature, remember you're asking a lot of it.

How it runs

The node shells out to UniMate's CLI:

python -m unimate.inference.sample --exp_dir <run> --model_path <ckpt> \
    --asset <prepared dir> --prompt "An object walks forward." \
    --num_repetitions 1 --cfg_scale 3.0 --seed 0 --only_save_motion \
    --output_dir <output/unimate_motion/<stamp>>

That's ComfyUI's own Python, not a second environment - it already has torch, einops, scipy and the rest, and the only thing missing is tyro, the CLI's argument parser, which is the pack's single pip dependency. If tyro is missing from both interpreters, you get a message telling you to install the requirements. Weights load per run; for a 74M model that's a couple of seconds, not a couple of minutes. --only_save_motion skips the Blender-dependent driving step, because the export node does that job itself.

What comes back on the motion output is a handle, not a tensor - paths to the generated .npy clips, the asset directory, and fps 30. Export turns it into bone rotations. info tells you how many clips, how many frames each, how long it took, and your cfg and seed.

Prompt like the training captions

All 13,769 captions UniMate was trained on begin with "An object" and describe one motion. Not the character - the motion. So:

An object walks forward.

beats

A red dragon flies over a castle.

every time. You're not casting a character here; the character is already decided, it's your mesh. You're selecting a motion. An object runs, An object jumps, An object crawls - that register.

The knobs

  • prompt - multiline, and the line breaks are load-bearing (see below).
  • cfg_scale - default 3.0, range 1–10. This is the strongest lever you have. When a prompt is being ignored, 5–7 makes it take hold; lower it and the motion calms down. On a spider rig, going from 3 to 6 roughly doubled the per-frame foot motion.
  • num_repetitions - 1 to 16, how many clips to sample per run. You need this more than you think.
  • seed - the usual integer.
  • chain_overlap - default 10, only meaningful with a multi-line prompt.

And the honest warning the author puts in the tooltip deserves repeating: sample variation is not noise you can ignore, it's the whole game. Across four samples of one prompt on one rig, measured travel ranged from 0.02 to 3.4. Whether your creature actually goes anywhere is close to a coin flip per sample. Set num_repetitions to 4 or more and keep the winner, rather than re-rolling one at a time and losing track.

Longer than two seconds: one prompt per line

Clips are 60 frames - two seconds at 30 fps - and that's fixed. Every checkpoint was trained at max_motion_length = 60. You don't extend the window; you chain segments:

An object walks forward.
An object turns to the left.
An object jumps.

Each line is its own 2-second generation, and the sampler stitches them with UniMate's --motion_expand: segment n's first chain_overlap frames are pinned to the last chain_overlap frames of segment n-1, and the flow ODE denoises the rest. Continuity comes from the sampler, not from a blend you do afterwards. Length is 60 + (60 - chain_overlap) * (N - 1) frames - at the default overlap of 10, that's 2 s, 3.7 s, 5.3 s and 7 s for one through four lines.

Three things follow from that, and they're the source of most confusion. It's a storyboard, not a stretch - each line is a fresh generation, so repeat a line to extend one action rather than hoping a long sentence stretches. It's strictly sequential, so eight lines is eight sampling passes and it will not batch. And it needs cfg_scale above 1.0: at 1.0 the caption is zeroed, every segment after the first would be conditioned on nothing, so the node refuses instead of quietly handing you noise. If a transition snaps, raise chain_overlap to 15–20.

When it comes back empty

The failure mode you'll meet is "the sampler wrote no motions". The most common cause is a rig with more joints than the checkpoint accepts - UniMate's sampler refuses that silently, which is precisely why Mesh Preparation checks the count and fails early. Go read the preparation report. Everything else here is a tuning problem; that one is a rig problem.

Category3D/UniMate

Inputs (7)

NameTypeDefaultDescription
modelUNIMATE_MODEL—
assetUNIMATE_ASSET—
promptSTRINGAn object walks forward.Write like the training captions: start with 'An object' and describe ONE motion, not the character. ONE PROMPT PER LINE chains the clip: each line is another 2 s segment, stitched into one long motion.
cfg_scaleFLOAT3.01–10How strongly the prompt is followed. Raise to 5-7 when a prompt is ignored; lower for calmer motion.
num_repetitionsINT11–16Samples vary a lot -- whether a creature actually travels is close to a per-sample lottery. Generate several and keep the best.
seedINT00–4294967295—
chain_overlapoptINT101–59Frames shared between consecutive segments when the prompt has several lines. The model blends the handover across them, so raise it (15-20) when a transition snaps. Ignored for a single-line prompt.

Outputs (2)

NameTypeDescription
motionUNIMATE_MOTION—
infoSTRING—