Nodes/HDM-ext/HDM Camera
ComfyUI Node

HDM Camera

Pan and zoom without a ControlNet

By KohakuBlueleaf·Created about a year ago·Updated 9 months ago· 41
HDM Camera
  • positive
  • negative
  • CONDITIONING
  • CONDITIONING
x_shift0.00
y_shift0.00
zoom1.00

You don't need ControlNet to move the camera in HDM. That's the whole point of this node. The HDM model (KohakuBlueleaf's home-trained anime model) learned camera motion by itself, and the HDM Camera node just exposes the dials.

Here's the story. During training, HDM used a "shifted square crop" strategy - randomly offsetting the crop region while training. The model picked up on the pattern, and the release notes brag about the payoff: "emergent capabilities such as intuitive camera control through position map manipulation... without additional conditioning." No ControlNet weights, no extra training, no API - the camera control came out of the training recipe for free.

How it works

This node is the "position map manipulation." It writes three numbers into your conditioning, and the HDM model's loader has a matching reader (extra_conds) that turns them into tensors the transformer's forward pass actually uses. Look inside the model and you can see the mechanism: it builds an axial position map for the image, then literally adds x_shift to the x positions, adds y_shift to the y positions, and divides the whole map by zoom. Pan and push-in, applied directly to the positional embeddings. Cheap, deterministic, and it only works because the model was trained to understand it.

It sits between your text encoders and the KSampler. In the shipped example workflow the chain is: CLIPTextEncode → HDM Camera → HDM Tread Gamma → KSampler. It takes your positive and negative conditioning in, stamps the camera values on both, and hands them back.

The inputs that matter

  • positive / negative (CONDITIONING) - your encoded prompts, passed through untouched. One of each in, one of each out.
  • x_shift (FLOAT, default 0, range −5 to 5) - pans along one axis. Step of 0.01, so it's a fine dial.
  • y_shift (FLOAT, default 0, range −5 to 5) - pans the other axis.
  • zoom (FLOAT, default 1, range 0.2 to 5) - scales the position map. 1 is neutral.

Note that the same values get applied to positive and negative alike - there's no separate camera per stream. If you want per-side control, that's what the Tread Gamma node is for.

Both outputs are CONDITIONING, wired straight into KSampler.

Using it without getting fooled

The effect is subtle and resolution-dependent. At 1024px even x_shift: 0.5 is a gentle pan, so nudge in small steps and compare at a fixed seed. This is also a per-image parameter - there are no keyframes. To fake a pan, re-run with a shifted x_shift per image (batch if your VRAM allows) and stitch the frames.

And one trap worth knowing: this node does nothing on non-HDM models. It only writes extra keys into the conditioning dict, and ordinary ComfyUI models ignore keys they don't recognize. Wire it into an SDXL workflow and it silently passes your conditioning through unchanged - no error, no effect. Only use it downstream of the HDM Loader.

Installing

Same as any pack in the HDM-ext family: ComfyUI Manager → search "HDM" → install, or

cd ComfyUI/custom_nodes
git clone https://github.com/KohakuBlueleaf/HDM-ext

The dependency is transformers>4.51 (4.52+ for Qwen3), and the model file goes in ComfyUI/models/checkpoints - grab it from https://huggingface.co/KBlueLeaf/HDM-xut-340M-anime. Full install detail lives on the HDM Loader page; both nodes ship in the same pack, so install once and both appear.

It's an early, 0-impression node from a researcher you know from LyCORIS, and it's the most fun one in the pack. Five minutes of pan-and-zoom and you'll see why the author wrote the whole pack to show it off.

Categoryconditioning/hdm

Inputs (5)

NameTypeDefaultDescription
positiveCONDITIONING
negativeCONDITIONING
x_shiftFLOAT0.00-5–5
y_shiftFLOAT0.00-5–5
zoomFLOAT1.000.2–5

Outputs (2)

NameTypeDescription
CONDITIONINGCONDITIONING
CONDITIONINGCONDITIONING