Difforum · Camera (expressions)
The Deforum Math Syntax, Still Here and Still Useful
- params
- audio
- translation_x_curve
- translation_y_curve
- translation_z_curve
- rotation_3d_x_curve
- rotation_3d_y_curve
- rotation_3d_z_curve
- zoom_curve
- camera
- info
The Difforum pitch is "no camera expressions to write". They're right that most people shouldn't need them. But if you came from Deforum, this node is the comfort blanket: the same 0:(expr) syntax on seven axes, backed by a safe expression evaluator rather than eval().
The syntax
Each axis takes a Deforum-style schedule where the keys are frames and the values are expressions:
0:(0), 60:(2*sin(2*pi*t/30))
t is the current frame and s is seconds. pi, sin, cos, abs, min, max and friends are available. When audio is connected, so are the six bands - amp, low, mid, high, onset, beat.
Here's the part that trips people up, and it's the same trap as the original: values are per-frame increments, not positions. A constant translation_x = 0:(2) doesn't put the frame at x=2, it pans a bit every single frame, and the result accumulates through the feedback loop. That's the whole design - it's why a schedule of motions works at all - but it means "2" and "0.2" are wildly different moves. zoom follows the same logic: it's a per-frame scale factor, so 0:(1.0 + 0.004*sin(2*pi*t/48)) is a gentle breathing zoom, and 0:(1.01) is 1% closer every frame, which gets silly fast.
The default rotation_3d_z of 0:(0.3) is a nice illustration: a third of a degree of roll per frame, which over 120 frames is a full 36 degrees of rotation. Subtle constant, big cumulative result.
Inputs
Seven axes: translation_x, translation_y, translation_z (3D only), rotation_3d_x (tilt), rotation_3d_y (pan), rotation_3d_z (roll), and zoom. Plus:
mode-2dor3d, matching the rest of the pack. In 3D you want a depth map downstream or you're back to pseudo-3D.fov(40) - lens field of view in degrees. 20–30 is long lens, 40 normal, 60+ wide. This matters more than people expect: it's what makes a dolly feel like a dolly rather than a zoom.audio- makes the bands available inside expressions.- Seven
*_curvesockets - aDIFFORUM_SCHEDULE(an Audio Curve, or a Schedule) replaces the text for that axis. This is the useful trick: write the shapes you like by hand, and hand the axis you want reactive over to a schedule you can tweak without touching the text.
Outputs
camera and info. camera overrides the Director's camera wherever it plugs in - Feedback Sampler, Guide Frames, Storyboard, Camera → Prompt, Camera Export. The info string is worth reading at least once per session; it reports the resulting motion range, which is how you catch "I typed 2 instead of 0.2" before rendering.
Install
Manager → search "Difforum", or:
cd ComfyUI/custom_nodes && git clone https://github.com/chillithebillis/Difforum.git difforum
Restart. numpy and a built-in AST evaluator - notably no eval(), which is a real difference from the 2022-era scripts people copy expressions out of.
Where people get burned
Motion through space needs depth. Pan, roll and zoom are in-plane and work on a flat image; dolly, orbit and tilt are camera-in-a-room moves, and without a depth map the engine falls back to a pseudo-3D affine - dolly becomes zoom. It works, it just won't have the parallax you're picturing, and the report says so.
The other one is continuity across transition. Expressions are per-frame increments, so a discontinuity in the expression is a jump in the image. If you build a move out of piecewise segments, ease them into each other or you'll get a visible snap - which is the same problem the Camera (keys) node solves with its own transition slider.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| params | DIFFORUM_PARAMS | From Setup: frame count, fps, width and height. Plug the same Setup everywhere. | |
| mode | COMBO | 2d | 2d = flat moves; 3d = parallax with a depth map. |
| fov | FLOAT | 401–170 | Lens field of view in degrees. 20-30 long lens, 40 normal, 60+ wide. |
| translation_x | STRING | 0:(0) | Horizontal move per frame. Deforum syntax: '0:(0), 60:(2*sin(t/10))'; t = frame. |
| translation_y | STRING | 0:(0) | Vertical move per frame, same syntax. |
| translation_z | STRING | 0:(0) | Forward (+) / back (-) per frame. 3D only. |
| rotation_3d_x | STRING | 0:(0) | Tilt, degrees per frame. 3D only. |
| rotation_3d_y | STRING | 0:(0) | Pan (turn), degrees per frame. 3D only. |
| rotation_3d_z | STRING | 0:(0.3) | Roll, degrees per frame. |
| zoom | STRING | 0:(1.0 + 0.004*sin(2*pi*t/48)) | Scale per frame. 1.0 = none, 1.01 = 1% closer each frame. |
| audioopt | DIFFORUM_AUDIO | Audio curves usable in expressions: amp, low, mid, high, onset, beat. | |
| translation_x_curveopt | DIFFORUM_SCHEDULE | A schedule (Audio Curve, Schedule) that replaces the text for this axis. | |
| translation_y_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. | |
| translation_z_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. | |
| rotation_3d_x_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. | |
| rotation_3d_y_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. | |
| rotation_3d_z_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. | |
| zoom_curveopt | DIFFORUM_SCHEDULE | A schedule that replaces the text for this axis. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| camera | DIFFORUM_CAMERA | — |
| info | STRING | — |