Number Expression
A real calculator node, no Python sandbox required
- a
- b
- c
- d
- e
- f
- g
- h
- i
- j
- k
- l
- m
- n
- o
- p
- q
- r
- s
- t
- u
- v
- w
- x
- number
- float
- int
- boolean
- text
Every ComfyUI user eventually needs to do math that the primitive math nodes can't express in one step. Number Expression is the node that lets you write (a * b) / 2 + c instead of chaining three Math nodes and squinting at the wires. You type a formula, it evaluates it, and the answer leaves as a NUMBER, a FLOAT, an INT, a BOOLEAN and text all at once.
It's the middle ground between ComfyUI's fiddly single-operation math nodes and the "power user runs a Python node" approach. The nice bit is the sandboxing: this is not a general-purpose code runner. Only arithmetic reads - the allowed function list is short and named, and anything else is refused by name before it runs. If you've ever pasted a workflow containing a PythonScript node you didn't fully trust, you can see why that's a feature.
The expression language
Slots a through x give you 24 numbers, each typed in or wired from elsewhere. A slot the expression never mentions is simply ignored; unconnected slots use their widget value. Then the expression box is your formula:
(a * b) / 2 + c
clamp(a, 0, 1)
round(a / b, 2)
a if a > b else b
Functions available: min, max, abs, round, floor, ceil, sqrt, clamp(v, lo, hi), lerp(a, b, t), sign, log, log2, log10, exp, sin, cos, tan, atan2, hypot, degrees, radians. Constants pi, e and tau are there, comparisons work, and and/or combine conditions. The expression box takes several lines joined into one, and # starts a comment.
The boolean output deserves a mention: a > b answers 1 or 0 on the number outputs, but the BOOLEAN socket carries the real true/false - wire that into a switch and you've built a comparison node too.
Outputs, and one honest caveat
number- the answer on the NUMBER wire, whole where it came out whole.float/int- the same answer as a decimal and as an integer (int cuts the fraction off rather than rounding, so 3.9 leaves as 3).boolean- false when the answer is 0, true otherwise.text- the answer written out (4.5or7), which is what you feed into a filename prefix or a text join.
Watch the int behavior: round(3.7) gives 4.0 on the float side but 3 on the int side because int truncates. Use round() if you actually want rounding - the tooltips make the distinction explicit and it's a real trap.
Setup and gotchas
decimals sets how many places a fractional answer is rounded to (6 = 0.333333, 2 = 0.33, 0 = whole). on_error decides what happens when the expression is refused or impossible: error stops the run and names the cause; zero logs it and answers 0. The one genuinely foot-gun case is a / b with b at 0 - that stops the run unless on_error is zero.
This node ships in WAS Node Suite v3 - install via ComfyUI Manager (search WAS Node Suite v3) or:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui
Restart after cloning; you need ComfyUI 0.14.0+ and Python 3.10+. No extra packages - v3's rewrite deliberately dropped the pack's old twenty-dependency install, and nothing gets downloaded on startup.
Honestly, the use case that keeps this node on my canvas is filenames and captions. Build a frame number or a scaled value, push the text output into a save prefix, and every render names itself correctly.
Inputs (27)
| Name | Type | Default | Description |
|---|---|---|---|
| expression | STRING | a + b | The formula, over `a` to `x`. Eg: `(a * b) / 2 + c`. Functions: min max abs round floor ceil sqrt clamp lerp sign log log2 log10 exp sin cos tan atan2 hypot degrees radians, plus pi, e and tau. `a > b` comes out as 1 or 0; `#` starts a comment. |
| aopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `a` stands for. Type it here or wire one in. Unconnected slots use the widget, and a slot the expression never names is ignored. |
| bopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `b` stands for. Type it here or wire one in. `a / b` with b at 0 stops the run unless on_error is set to zero. |
| copt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `c` stands for. Type it here or wire one in. Handy as the offset in `(a * b) / 2 + c`. |
| dopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `d` stands for. Type it here or wire one in. The fourth value, free for a limit such as `clamp(a, c, d)`. |
| eopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `e` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| fopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `f` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| gopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `g` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| hopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `h` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| iopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `i` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| jopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `j` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| kopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `k` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| lopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `l` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| mopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `m` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| nopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `n` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| oopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `o` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| popt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `p` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| qopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `q` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| ropt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `r` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| sopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `s` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| topt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `t` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| uopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `u` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| vopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `v` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| wopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `w` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| xopt | FLOAT,NUMBER,INT | 0-18446744073709550000–18446744073709550000 | The number `x` stands for. Type it here or wire one in. An unconnected slot uses its widget, and a slot the expression never names is ignored. |
| decimalsopt | INT | 60–15 | Decimal places a fractional answer is rounded to. 6 = 0.333333, 2 = 0.33, 0 = whole, so 3.7 comes out 4.0. It also clears the trailing 0.0000000001 that decimal arithmetic leaves behind. A whole answer is untouched. |
| on_erroropt | COMBO | error | What a refused or impossible expression does. `error` = stop the run and name the cause, `zero` = log it and answer 0. Pick `zero` where a division by zero is expected on some frames of a batch. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| number | NUMBER | The answer on the NUMBER wire, whole where it came out whole. A comparison answers 1 or 0. |
| float | FLOAT | The same answer as a decimal, so 7 leaves here as 7.0. |
| int | INT | The same answer with its fraction cut off rather than rounded, so 3.9 leaves here as 3. Held to the range a whole-number socket carries. |
| boolean | BOOLEAN | false when the answer is 0, true for anything else. Wire it to a switch to branch on `a > b`. |
| text | STRING | The answer written out, as `4.5` or `7`. Feed it to a filename prefix or a text join. |