Nodes/ComfyUI-BCNodes/Math Expression
ComfyUI Node

Math Expression

Sizes, seeds and step counts without a calculator

By 0xBeycan·Created 23 days ago·Updated 3 days ago· 2
Math Expression
  • a
  • b
  • c
  • INT
  • FLOAT
◄expression►

Half of ComfyUI plumbing is one number that has to be derived from another. "Width should be 1.5× the latent's width." "Steps should be whatever I set, times two." Math Expression is the node that lets you write that as an expression instead of a chain of multiplier primitives.

What it is

A multiline expression widget, three optional inputs (a, b, c) that accept an INT, FLOAT, IMAGE or LATENT each, and two outputs: INT and FLOAT carrying the same value, truncated and as a float. The result is also drawn on the node, so you can read it without wiring anything.

The expressions people actually write:

a.width * 1.5
min(a.width, 2048)
steps * 2
iif(a > b, a, b)
randomint(1, 1000)

How it works

Here's the bit worth caring about: the expression is parsed with Python's ast module and walked with a whitelist. There is no eval() - a node that evaluates arbitrary strings from a workflow file is a node that runs arbitrary code from a downloaded workflow, and this one doesn't. Anything outside the whitelist raises a ValueError that names the offending piece, which makes the errors unusually readable for a node like this.

What's allowed:

  • numbers, and + - * / // % **, the bitwise & | ^ << >>, unary - + ~ not, and and / or
  • comparisons (== != < <= > >=, chained) which yield 1 or 0 - so a > b gives you a number, not a boolean
  • the names a, b, c
  • a.width and a.height for an IMAGE or LATENT input; latent sizes are multiplied by 8, i.e. it hands you the pixel size you were about to guess at
  • NodeTitle.widget to read another node's widget by node title or type
  • the functions min max abs round int float pow sqrt floor ceil randomint randomchoice iif

An empty expression evaluates to 0. Referencing an input that isn't connected is an error, not a silent zero.

What it's good for

The a.width access is the reason I reach for it. Working at one resolution and upscaling by a fixed factor is a chain of guesswork otherwise - you generate at 1024, decide to go 1.5×, and now every resize widget in the graph says 1536 and drifts out of sync the moment you change the first one. Wire the latent into a, put a.width * 1.5 in the box, and the whole thing follows. Anything where the correct answer is "derived from the image I'm actually holding" belongs here.

Second use: making a randomised number that isn't a seed - randomint(1, 10) for a pick-one-of-ten style, or iif() to choose between two constants based on a checkbox.

Installing it

ComfyUI Manager → search ComfyUI-BCNodes → install → restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/0xBeycan/ComfyUI-BCNodes
pip install -r ComfyUI-BCNodes/requirements.txt

Reload the browser tab. It's under BCNodes/logic; search BCNodes to see the pack's nodes. No extra dependencies - requirements.txt only covers the pack's PostFx and Caption Audit nodes.

Common issues

It's an output node, so it always runs. That's deliberate: it's how the value can be displayed on the canvas with nothing wired to it. The side effect is that ComfyUI's cache can't skip it, and therefore can't skip anything upstream of it either. If you hang one of these off a big IMAGE chain for a bit of arithmetic, expect that chain to execute on every queue. Keep the expression downstream of the cheap stuff.

Renaming a node breaks NodeTitle.widget. The reference is by title or type, not by identity. Rename KSampler and the expression that read KSampler.steps stops working.

The INT output truncates. 2.9 becomes 2, not 3. If you want rounding, say so - round(x) is in the list.

No subscripts, no strings, no lambdas. a[0] is a ValueError. This is a small arithmetic language, not a scripting surface.

CategoryBCNodes/logic

Inputs (4)

NameTypeDefaultDescription
expressionSTRING—
aopt*—
bopt*—
copt*—

Outputs (2)

NameTypeDescription
INTINT—
FLOATFLOAT—