More Math
Adds math nodes for numbers and types which do not need it. Inspired by the was_extras node.
There is workflow folder containing examples. It will expand.
The most important thing is that last part of input must be an expression. Node uses it to output result. It can be constant, function call, variable or mixture of these. No loops, branching using if. Branches, loops, ... can be before the final expression.
Quickstart
- Install ComfyUI.
- Clone this repository into
ComfyUI/custom_nodes. - open command prompt/terminal/bash in your comfy folder
- activate environment
./venv/Scripts/activate - go to more_math folder
cd ./custom_nodes/more_math/ - install requirements
pip install -r requirements.txt - Restart ComfyUI.
You can also get the node from comfy manager under the name of More math.
Features
- Functions and variables in math expressions.
- Conversion between
INTandFLOAT,INTandBOOLEAN, andAUDIOandIMAGE- image/audio conversion encodes frequency content into RGB channels.
- Node support for
FLOAT,STRING,CONDITIONING,LATENT,IMAGE,MASK,NOISE,AUDIO,VIDEO,MODEL,CLIP,VAE,SIGMAS, andGUIDER. - Vector math with list literals
[v1, v2, ...]and mixed operations between lists, scalars, and tensors. - Custom functions
funcname(variable, variable, ...)->expression;- can be used later in expressions and in other custom functions,
- shadowing built-in functions is not supported,
- recursion is allowed, so be careful.
- Custom variables
varname=expression;- can be used later in the same expression chain,
- compound assignments are supported:
+=,-=,*=,/=,%=.
- Indexed assignment
a[i, j, ...] = expression;- the first write to a tensor copies it, so node inputs are never changed in place,
- supports multidimensional tensors and nested lists,
- scalar values and 1-element tensors/lists fill the selected slice,
- leading singleton dimensions are squeezed when needed to match target rank.
- python-like slicing
a[start:end]ora[start:end:step]for 1D slices,a[start1:end1, start2:end2, ...]for N-D slices (step can also be specified),- negative indices count from the end of the dimension.
- json-like dictionary literals
{key1: value1, key2: value2, ...}for structured data. Keys can be strings or numbers or variables (variable will be evaluated to its value), values can be any expression. Indexing using []. - Control flow:
if/elsewhilefor- blocks
{ ... } returnbreakcontinue
- Stack support shared across evaluations through stack connections.
- useful for
GuiderMathstate between steps, - with no stack connected, every run starts with an empty stack.
- useful for
- Comments:
- line comments
# ... - block comments
/* ... */
- line comments
- When a tensor is expected, lists can often be used instead and will be promoted automatically.
Control Flow Statements
- Conditions (
if,while, ternary) are true when the value, or any of its elements, is non-zero. On a tensor the ternary picks per element instead. - If/Else:
if (condition) statement [else statement] - While Loops:
while (condition) statement - Blocks:
{ statement1; statement2; ... }- New variables defined in blocks are isolated and don't leak to outer scope
- Modifications to existing variables persist to outer scope
- Return Statements:
return [expression];- Returns from the current function; at top level it ends the script with that value
- For Loops:
for (variable in expression) statement- Iterates over list elements
- Iterates over tensor elements along dimension
0 - Scalars are treated as one-item iterables
- Break/Continue:
break;,continue;- Control loop execution (works in
whileandforloops) - Only affect loops in the same function; using them outside a loop is an error
- Control loop execution (works in
- Scripts can be cancelled with ComfyUI's interrupt, even inside endless loops or recursion.
Operators
- Math:
+,-,*,/,%,^,|x|(abs / norm-style magnitude) - Assignment:
=,+=,-=,*=,/=,%=- also works on indexed targets, for example
a[i] += 1;ora[i, j] = expression;.
- also works on indexed targets, for example
- Boolean:
<,<=,>,>=,==,!=(false = 0.0,true = 1.0) - Ternary:
condition ? a : b - Constants:
pi/PI,e/E(case-insensitive). none/None/null/NULL: empty/no-value literal.- Bitwise shifts:
<<,>> - Indexing:
x[i]orx[i, j, ...]- works on tensors, nested lists, and strings,
- supports scalar, tensor, and list indices.
- Lists:
[v1, v2, ...]- vector math is supported,
- lists can broadcast across tensor operations in many cases,
- lists can be used for color triples, coordinates, kernel sizes, and permutations.
- Length Mismatch Handling: All math nodes (except Model, Clip, Vae which default to broadcast) include a
length_mismatchoption to handle inputs with different batch sizes, sample counts, or list lengths. The target length is determined by the maximum length among all provided inputs (a,b,c,d).do nothing: dones no validation on inputtile: Repeats shorter inputs to match the maximum length.error(Default): Raises aValueErrorif any input lengths differ.pad: Shorter inputs are padded with zeros to match the maximum length.
Functions
1) Core Math
1.1 Elementary & Numeric
abs(x)or|x|: absolute value; tensor/list inputs are handled element-wise.sqrt(x): square root.ln(x): natural logarithm.log(x): base-10 logarithm.log2(x): base-2 logarithm.exp(x): exponentiale^x.log1p(x): numerically stableln(1 + x).expm1(x): numerically stableexp(x) - 1.logaddexp(x, y): numerically stableln(exp(x) + exp(y)), element-wise.sinc(x): normalized sinc,sin(πx)/(πx); returns1at zero.cbrt(x): real cube root, including for negative values.reciprocal(x): element-wise1 / x.isnan(x): per-element NaN test.isinf(x): per-element infinity test.isfinite(x): per-element finite-value test.isclose(a, b, [rtol], [atol]): elementwise approximate comparison.allclose(a, b, [rtol], [atol]): returns1.0when all values are approximately equal, otherwise0.0.pow(x, y): powerx^y.floor(x): round down.ceil(x): round up.round(x): round to nearest integer.fract(x): fractional part (x - floor(x)).sign(x): sign (-1,0,1).gamma(x): gamma function.clamp(x, min, max): clamp to interval.step(x, edge): returns1whenx >= edge, otherwise0.dist(x1, y1, x2, y2)/distance: Euclidean distance between 2D points.hypot(x, y): element-wise Euclidean distance from the origin, computed robustly assqrt(x*x + y*y).
1.2 Trigonometric
sin(x): sine in radians.cos(x): cosine in radians.tan(x): tangent in radians.asin(x): inverse sine.acos(x): inverse cosine.atan(x): inverse tangent.atan2(y, x): quadrant-aware inverse tangent.degrees(x): convert radians to degrees.radians(x): convert degrees to radians.
1.3 Hyperbolic
sinh(x): hyperbolic sine.cosh(x): hyperbolic cosine.tanh(x): hyperbolic tangent.asinh(x): inverse hyperbolic sine.acosh(x): inverse hyperbolic cosine.atanh(x): inverse hyperbolic tangent.
1.4 Activation / ML
relu(x):max(0, x).silu(x): Sigmoid Linear Unit, equivalent tox * sigm(x); applied element-wise.gelu(x): Gaussian Error Linear Unit.leaky_relu(x, [negative_slope]): Leaky ReLU;negative_slopedefaults to0.01.softplus(x): smooth ReLU-like function.softsign(x): bounded activationx / (1 + abs(x)).sigm(x): sigmoid.softmax(x, [dim]): softmax normalization.softmin(x, [dim]): softmin normalization.erf(x): error function.erfc(x): complementary error function,1 - erf(x), evaluated directly for better tail accuracy.erfinv(x): inverse error function.group_norm(x, groups, [weight], [bias], [eps]): PyTorch GroupNorm for channel-first tensors shaped[N, C, ...];groupsmust divideC, affine parameters are optional vectors of lengthC, andepsdefaults to1e-5.
1.5 Logical
logical_not(x): element-wise logical NOT; returns1.0wherexis zero and0.0otherwise.logical_and(x, y): element-wise logical AND; returns1.0where both values are non-zero and0.0otherwise.logical_or(x, y): element-wise logical OR; returns1.0where either value is non-zero and0.0otherwise.
1.6 Interpolation & Remap
lerp(a, b, t): linear interpolation.slerp(a, b, t, [dim]): spherical interpolation of vector directions with linearly interpolated magnitudes. Withoutdim, all elements form one vector. Setdimto select an axis of vector components; all other axes identify independent vectors, andtmay be scalar or a tensor broadcast over those axes.- With a list
t,lerp,slerpand the*_easefunctions return one result per time (as doesinterpwith a listx); list inputs from an earlier call are paired with their time, so nested curves work. Join the results into a batch withconcat(..., 0), e.g.concat(slerp(slerp(V0, V1, w), slerp(V2, V3, w), w), 0). smoothstep(x, e0, e1): 3rd-order smooth transition.smootherstep(x, e0, e1): 5th-order smoother transition.cubic_ease(a, b, t)/cubic: cubic easing interpolation.sine_ease(a, b, t)/sine: sine easing interpolation.elastic_ease(a, b, t)/elastic: elastic easing interpolation.remap(v, i_min, i_max, o_min, o_max): map value from one interval to another.interp(x, xs, ys, [mode]): keyframe interpolation through the points(xs, ys), holding the first/last value outside the keys. Modes:"linear"(default),"smooth"(eases in and out of every key),"step"(holds a value until the next key).xcan be a tensor; whenxis a number the values can be tensors, e.g. keyframe images. Example:interp(frame, [0, 24, 48], [0.2, 1, 0.5]).
2) Tensor, Stats & Data Ops
2.1 Element-wise / Tensor Math
tmin(x, y): element-wise minimum.tmax(x, y): element-wise maximum.tnorm(x, [dim]): L2 normalization along the last dimension by default, or alongdim; list input returns a nested list and tensor input returns a tensor.snorm(x, [dim]): L2/Frobenius norm of a scalar, list, or tensor. Withdim, reduces along one or more dimensions.norm(x, [p], [dims]): p-norm globally or along one or more dimensions;pdefaults to2.normalize(x, [p], [dim], [eps]): normalize each vector alongdim(default-1); defaults to L2 and clamps the divisor toeps(1e-12). List input returns a nested list; tensor input returns a tensor.cossim(a, b)/cosine_similarity: cosine similarity.cov(x, y): sample covariance of the flattened inputs.corr(x, y)/correlation: Pearson correlation.entropy(x): Shannon entropy ofsoftmax(x)(xis treated as logits).flip(x, dims): flip selected dimensions;dimscan be a single dimension, list, or tensor of dimensions, so multiple axes can be flipped at once.swap(tensor, dim, i1, i2): swap two indices along dimensiondim.
2.2 Reductions & Statistical Aggregates
sum(x, [dims]): sum; optionaldimscan be a single dimension or a list of dimensions.mean(x, [dims]): mean globally or along dimensions.std(x, [dims]): sample standard deviation globally or along dimensions; returns0when a reduction contains fewer than two values.var(x, [dims]): sample variance globally or along dimensions; returns0when a reduction contains fewer than two values.prod(x, [dims]): product globally or along dimensions.logsumexp(x, [dims]): numerically stablelog(sum(exp(x))), globally or along dimensions.median(x, [dim]): median globally or along one dimension; withoutdim, reduces all elements.mode(x, [dim]): mode globally or along one dimension; withoutdim, reduces all elements.quartile(x, k, [dim])/quartil: quartile (kin0..4), globally or along one dimension.percentile(x, p, [dim])/prcnt: percentile (pin0..100), globally or along one dimension.quantile(x, q, [dim]): quantile (qin0..1), globally or along one dimension.moment(x, a, k): k-th moment around centera.any(x):1.0if any element is non-zero.all(x):1.0if all elements are non-zero.count(x)/length(x)/cnt(x): number of elements or length.cumsum(x, [dim]): cumulative sum;dimdefaults to0.cumprod(x): cumulative product along dimension0.smin(x, ...[, dims]): scalar minimum across inputs; optionally reduce the first tensor/list input acrossdims.smax(x, ...[, dims]): scalar maximum across inputs; optionally reduce the first tensor/list input acrossdims.weighted_mean(x, weights, [dims]): computessum(x * weights) / sum(weights)globally or along one or more dimensions; raises an error if a reduction's weights sum to zero.
2.3 Sorting / Selection / Indices
sort(x, [descending], [dim]): tensors are sorted along the specified dimension (dim); ifdimis not specified, it defaults to the last dimension (dim=-1); lists use Python sorting.argsort(x, [descending], [dim]): returns indices alongdim(defaults to last dimension);descendingdefaults tofalse.argmin(x, [as_position], [dim]): global minimum index, or indices alongdim;as_positionreturns global coordinates and cannot be combined withdim.argmax(x, [as_position], [dim]): global maximum index, or indices alongdim;as_positionreturns global coordinates and cannot be combined withdim.topk(x, k, [dim]): tensors keep the same shape and all non-top-k values are zeroed; defaults to the last dimension. Lists return the sorted top-k slice.botk(x, k, [dim]): same astopk, but for smallest values.topk_ind(x, k, [dim])/topk_indices: top-k indices alongdim; withoutdim, uses global indices fromflatten().botk_ind(x, k, [dim])/botk_indices: bottom-k indices alongdim; withoutdim, uses global indices fromflatten().unique(x): sorted unique values for tensors and lists.where(cond, a, b): tensor broadcasting for tensor inputs; recursive truthy selection for lists/scalars.histogram(x, bins, min, max)/hist: histogram counts fromflatten()in the given intervalgather(tensor, dim, indices): gather values alongdimusingindicesscatter(tensor, dim, indices, src): scatter values fromsrcalongdimusingindicesscatter_add(tensor, dim, indices, src): scatter-add values fromsrcalongdimusingindicesnonzero(x): returns coordinate indices for non-zero elements.one_hot(indices, classes): converts integer class indices into float32 one-hot vectors; indices must be in0..classes-1.
2.4 Shape / Structure / Layout
shape(x): returns shape as a Python list.flatten(x): flattens tensor or nested list.reshape(tensor, shape)/rshp: reshape with element-count validation.squeeze(x, [dim]): removes singleton dimensions; optionaldimfollows PyTorch semantics.unsqueeze(x, dim): inserts a singleton dimension atdim.transpose(x, dim0, dim1)/swapaxes: swaps two dimensions.permute(tensor, dims)/perm: reorder dimensions.crop(tensor, position, size): extract a region from tensor or string.pad(tensor, padding): pad tensor.overlay(base, overlay, offset, [opacity], [mode]): overlay one value/tensor onto another. For tensors,modeapplies a blend mode fromblend()to the pasted region.append(a, b): append or concatenate items, lists, and tensors.repeat(x, count, [dims]): repeat values; tensorcountcan be a scalar or a per-dimension list whendimsis provided.batch_shuffle(tensor, indices)/shuffle/select: reorder along batch dimension.concatenate(..., dim)/concat/cat: concatenate tensors or lists; a single list of tensors,concat(list, dim), concatenates its elements.stack(tensors, [dim])/stack(x, ..., dim): stack a non-empty list of tensors, or separate tensors followed bydim, along a new dimension.roll(tensor, shifts, [dim]): circular shift.tensor(shape, [value, [type]]): create a filled tensor;typea tensor to copy its dtype to self when being created.zeros(shape, [dtype_template])/ones(shape, [dtype_template]): create zero- or one-filled tensors; an optional tensor supplies the output dtype.eye(n, [dtype_template]): create ann-by-nidentity matrix; an optional tensor supplies the output dtype.interpolate_linear(tensor, scale): linear interpolation-based resizing. Scale can be a single float or a list. When it is a list, it is interpreted as the target output size.interpolate_area(tensor, scale): area interpolation-based resizing.interpolate_nearest(tensor, scale)/interpolate_nearest_exact: nearest neighbor interpolation-based resizing.
2.5 Linear Algebra
dot(a, b): dot product after flattening.outer(a, b): outer product of flattened inputs.matmul(a, b): matrix multiplication.cross(a, b): cross product; last dimension must be3.trace(x): sum of the main diagonal of each square matrix.det(x): determinant of each square matrix. Singular square matrices return0; non-square inputs or inputs with fewer than two dimensions raise an error because their determinant is undefined.solve(A, b): solve the square linear systemA*x = b; prefer this to computinginv(A) @ b.matrix_rank(x, [tol]): numerical rank of each matrix; optionaltolis the absolute singular-value tolerance.inv(x, [pseudo]): matrix inverse;pseudodefaults to false. A singular or non-square matrix raises an error in ordinary mode. Setpseudoto a truthy value (for example,1) to compute the Moore–Penrose pseudoinverse, which also supports singular and rectangular matrices.pinv(x): inverse permutation (result[x[i]] = i); this is not the matrix pseudoinverse, useinv(x, 1)for that.singular_value_decomposition(x,[full_matrices])/svd: singular value decomposition.diagonal_matrix(x,shape,[offset],[dim1],[dim2])/diag: create a diagonal matrix from a vector. The diagonal can be offset from the main diagonal using theoffsetparameter. Thedim1anddim2parameters specify which dimensions to use for the diagonal.
2.6 Mapping / Sampling
map(tensor, c1, ...): coordinate remapping viagrid_sample.- supports up to
3coordinate inputs, - uses sampling coordinates derived from the provided coordinate tensors or functions,
- intended for spatial remapping / resampling.
- supports up to
get_value(tensor, position): read value at an N-D position using flat offset math. Superseded by tensor[position] indexing.
2.7 Losses / Image Metrics
mse(a, b, [reduction]): mean squared error; reduction ismean(default),sum, ornone(nonereturns element-wise values).mae(a, b, [reduction]): mean absolute error; reduction ismean(default),sum, ornone(nonereturns element-wise values).huber_loss(prediction, target, [delta], [reduction]): Huber regression loss;deltadefaults to1and reduction defaults tomean(sumandnoneare also supported).cross_entropy(logits, target, [dim], [reduction]): classification cross-entropy;targetmay contain class indices or probabilities matchinglogits. Class dimension defaults to1for multi-dimensional logits and0for a class vector; reduction defaults tomean.psnr(a, b, [data_range]): peak signal-to-noise ratio in decibels;data_rangedefaults to1and must be positive.
3) Imaging & Spatial Processing
3.1 Filters & Convolution
blur(x, sigma, [auto_channels])/gaussian: Gaussian blur using separable convolution. Ifauto_channelsis truthy, tries orienting the input such that channel is in the direction of the filter; otherwise it expects the color dimension to be last.edge(x, [kernel_size]): Sobel-style edge detection.ezconvolution(tensor, ...)/ezconv: convolution with auto layout handling.convolution(tensor, ...)/conv: direct convolution. Expects [batch, channel, spatial...] layout.
3.2 Mask Morphology
dilate(x, [kernel_size]): dilation.erode(x, [kernel_size]): erosion.morph_open(x, [kernel_size]): opening = erosion followed by dilation.morph_close(x, [kernel_size]): closing = dilation followed by erosion.distance_transform(mask, [threshold]): exact Euclidean distance in pixels from each pixel inside the mask (abovethreshold, default0.5) to the nearest pixel outside it; outside pixels are0. Usedistance_transform(1 - mask)for the distance outside the mask, e.g. for feathering.
3.3 Blending
blend(base, top, mode, [opacity]): blendstopontobaselike layers in an image editor; shapes broadcast. Modes:normal,multiply,screen,overlay,soft_light,hard_light,darken,lighten,difference,exclusion,add,subtract,color_dodge,color_burn. Useoverlay(..., mode)to place a smaller layer at an offset.
4) Optical Flow
rife(img1, img2, [tiling_size, iterations, multi_scale]): compute optical flow using a RAFT-based model.- uses the first checkpoint in
models/optical_flow; if there is none, torchvision's RAFT-large weights are downloaded once and saved there. tiling_sizechunks the image into overlappingtiling_sizextiling_sizetiles to conserve memory; defaults to0(auto-enables1024x1024tiling above 2MPx); a value between0and1is taken as a fraction of the resolution instead.iterationsdefaults to12.multi_scale(defaultfalse) also runs a low-resolution pass of the base image to help catch large movements.
- uses the first checkpoint in
motion_mask(flow): motion/occlusion mask from flow.flow_to_image(flow): visualize flow as RGB.flow_apply(image, flow): warp image by flow.flow_mag(flow)/flow_magnitude: flow vector magnitude (or anything else, uses first 2 positions of last dimension).flow_ang(flow)/flow_angle: flow vector angle in radians (atan2(dy, dx)). (or anything else, uses first 2 positions of last dimension)
5) Frequency Domain (FFT)
fft(x): fast Fourier transform across all dimensions.ifft(x, [shape]): inverse FFT.rfft(x, [dim]): real-input FFT alongdim(defaults to the last dimension).irfft(x, [n], [dim]): inverse real FFT;nanddimare optional.angle(x): phase angle of complex values.
6) Random & Noise
6.1 Random Distributions
- All random generators are seeded and deterministic for a given seed.
- All use the current node shape by default (based on shape of input to the node), but an optional trailing
shapeargument can be provided to specify a different output shape. - All use a
rand<dist>(...)/random_<distribution>(...)naming convention, but the argument list before[shape]differs per distribution - see each entry below. noise(seed, [shape])/randn/random_normal: normal distribution. Same generator as inRandom Noisenode.rand(seed, [shape])/randu/random_uniform: uniform distribution.rande(seed, lambda, [shape])/random_exponential: exponential distribution.randc(seed, median, sigma, [shape])/random_cauchy: Cauchy distribution.randln(seed, mean, std, [shape])/random_log_normal: log-normal distribution.randb(seed, probability, [shape])/random_bernoulli: Bernoulli distribution.randp(seed, lambda, [shape])/random_poisson: Poisson distribution.randg(seed, shape_param, scale, [shape])/random_gamma: gamma distribution.randbeta(seed, alpha, beta, [shape])/random_beta: beta distribution.randl(seed, loc, scale, [shape])/random_laplace: Laplace distribution.randgumbel(seed, loc, scale, [shape])/random_gumbel: Gumbel distribution.randw(seed, scale, concentration, [shape])/random_weibull: Weibull distribution.randchi2(seed, df, [shape])/random_chi2: chi-squared distribution.randt(seed, df, [shape])/random_studentt: Student’s t distribution.pink_noise(seed, [shape])/pink: deterministic pink noise with an approximately1/fpower spectrum.brown_noise(seed, [shape])/brown: deterministic brown noise with an approximately1/f^2power spectrum.- Pink and brown noise are normalized to zero mean and unit variance independently along the last dimension; shorter dimensions fall back to seeded white noise.
6.2 Procedural Noise
All procedural noises return values in 0..1.
perlin(seed, scale, [octaves, [offset, [shape]]])/perlin_noise:- smooth gradient noise,
- supports arbitrary dimensional grids.
voronoi(seed, scale, [jitter], [offset], [shape])/cellular/worley/voronoi_noise/cellular_noise:- cellular / Voronoi noise,
jitteris clamped to0..1.
plasma(seed, scale, [octaves, [offset, [shape]]])/turbulence/plasma_noise:- high-frequency turbulence-style noise.
ridged(seed, scale, [octaves, [offset, [shape]]])/ridged_noise:- ridged multifractal noise with sharp peaks and mountain-like structures.
domain_warp(seed, scale, warp_scale, warp_strength, [octaves, [warp_octaves, [offset, [shape]]]])/domain_warp_noise:- domain-warped noise for breaking up regular patterns and adding complex organic detail.
6.3 Noise Schedules
Same schedules as ComfyUI's scheduler nodes, as a tensor of sigmas for the SIGMAS node.
sigm_karras(steps, sigma_min, sigma_max, [rho]): Karras schedule,rhodefaults to7; ends with a final0.sigm_exponential(steps, sigma_min, sigma_max): evenly spaced in log sigma; ends with a final0.sigm_polyexponential(steps, sigma_min, sigma_max, [rho]): polynomial in log sigma,rhodefaults to1; ends with a final0.sigm_laplace(steps, sigma_min, sigma_max, [mu], [beta]): Laplace schedule,mudefaults to0andbetato0.5; like ComfyUI's Laplace scheduler it has no final0.
7) Bitwise
7.1 Shift Operators
a << b: Bitwise left shift.a >> b: Bitwise right shift.
7.2 Bitwise Functions
band(a, b)/bitwise_and: bitwise AND.bor(a, b)/bitwise_or: bitwise OR.bxor(a, b)/bitwise_xor: bitwise XOR.bnot(a)/bitwise_not: bitwise NOT.bitcount(a)/popcount/popcnt: number of set bits.
8) Strings
upper(str): convert to uppercase.lower(str): convert to lowercase.trim(str): trim surrounding whitespace.split(str, [delimiter]): split string.join(list, [separator]): join list into string.substring(str, start, [length])/substr: substring extraction.find(str, search): first match position.replace(str, search, replacement): replace occurrences.startswith(str, prefix): returns1.0if the string starts withprefix, otherwise0.0.endswith(str, suffix): returns1.0if the string ends withsuffix, otherwise0.0.regex_search(str, pattern): position of the first match of the regular expressionpattern, or-1.regex_replace(str, pattern, replacement): replace every regular-expression match;replacementcan refer to groups as\1.to_json(value)/json_encode(value): encode dictionaries, lists, and tensors as JSON. Tensors become nested arrays.from_json(text)/json_decode(text): decode JSON objects to dictionaries and arrays to lists. Tensor dtype and device are not preserved.format(value, [spec]): format numbers as text;specis a number of decimals (format(x, 2)) or a Python format spec (format(x, ".3e")). Defaults to the shortest form, so0.3stays"0.3". Lists and tensors are formatted per element.
9) Color
rgb_to_hsv(...): convert RGB to HSV.- accepts packed tensor/list input or separate
r, g, b, - optional trailing boolean argument enables hue in degrees - the 2nd argument in packed mode (
rgb, [degrees]), the 4th in separate mode (r, g, b, [degrees]).
- accepts packed tensor/list input or separate
hsv_to_rgb(...): convert HSV to RGB.- accepts packed tensor/list input or separate
h, s, v, - optional trailing boolean argument treats hue as degrees - the 2nd argument in packed mode (
hsv, [degrees]), the 4th in separate mode (h, s, v, [degrees]).
- accepts packed tensor/list input or separate
rgb_to_oklab(...): convert RGB to OKLab.- accepts packed tensor/list input or separate
r, g, b.
- accepts packed tensor/list input or separate
oklab_to_rgb(...): convert OKLab to RGB.- accepts packed tensor/list input or separate
L, a, b.
- accepts packed tensor/list input or separate
rgb_to_cielab(...): convert RGB to CIELAB.- accepts packed tensor/list input or separate
r, g, b.
- accepts packed tensor/list input or separate
cielab_to_rgb(...): convert CIELAB to RGB.- accepts packed tensor/list input or separate
L, a, b.
- accepts packed tensor/list input or separate
luminance(rgb)/luminance(r, g, b): Rec.709 luminance0.2126 R + 0.7152 G + 0.0722 B; the color dimension is removed.grayscale(image): replaces R, G, B with their luminance, keeping the shape and any alpha channel.int_to_rgb(value): convert packed integer color to RGB triplet.rgb_to_int(...): convert RGB triplet to packed integer color.
10) Utility & Conversion
print(x): print value and return it.print_shape(x)/pshp: print shape and return value.range(start, end, step): numeric sequence as a list.linspace(start, end, count): evenly spaced sequence.logspace(start, end, count, base): logarithmically spaced sequence.nan_to_num(x, nan, posinf, neginf)/nvl: replaceNaNand infinities.timestamp()/now: current Unix timestamp.int(x): convert to int32 or nested int values.float(x): convert to float or nested float values.as_nested(x)/as_nested_tensor: convert list to a nested tensor. Usefull for video+audio models. Values do not need to be in the same or compatible shape.
Generators
text_image(text, font, size, [max_width], [weight], [rotation_angle], [line_spacing], [italic], [underline])- renders text to an 2D tensorfontis a font file path or an installed font name; installed fonts are scanned once, so restart ComfyUI after installing new ones,- if the requested weight or italic face is missing, bold and italic are synthesized from the regular face.
coordinates(shape, dim, [dtype])/coords- generates a tensor whose values are the coordinates of each element along the specified dimension. dtype is copied from tensor at that position. Default is float32.
11) State / Stack
stack_push(id, value): push into stack slot.stack_pop(id): pop from stack slot.stack_get(id): read top value without removing it.stack_clear(id): clear stack slot.stack_has(id): check whether slot exists and is non-empty.
12) Dictionary
add_key(dict, key, value)- add a key-value pair to a dictionary.remove_key(dict, key)/rm_key- remove a key-value pair from a dictionary.keys(dict)- returns a list of dictionary keys in insertion order.
Variables
-
Common variables (except FLOAT):
-
D{N}- position in n-th dimension of tensor, for exampleD0,D1,D2
-
S{N}- size of n-th dimension of tensor, for exampleS0,S1,S2
-
V{N}- value input, for exampleV0,V1,V2
-
V- list of value inputs
-
F{N}- float input, for exampleF0,F1,F2
-
F- list of float inputs
-
FcntorF_count: number of float inputs
-
VcntorV_count: number of value inputsdepth: Current recursion depth (0 at top level)
-
common inputs (legacy):
a,b,c,d
-
Extra floats (legacy):
w,x,y,z
-
INSIDE IFFT
Forfrequency_count– frequency count (freq domain, iFFT only)Korfrequency- isotropic frequency (Euclidean norm of indices, iFFT only)Kx,Ky,K_dimN- frequency index for specific dimensionFx,Fy,F_dimN- frequency count for specific dimension
-
IMAGE and LATENT:
Corchannel- channel of imageX- X position in image, origin in top-leftY- Y position in image, origin in top-leftWorwidth- image widthHorheight- image heightBorbatch- batch indexTorbatch_count- number of batchesNorchannel_count- channel count
-
LATENT
V{N}_{M}- In case of nested tensors, takes the M-th tensor from N-th input. For example,V0_1is the second tensor from the first input.
-
MODEL, VAE, CLIP:
V{N}_d- difference against the original input (for example applied lora)Korkey- name of current layer. For example,proj_out.bias,diffusion_model.blocks.5.attn.out_proj.weightortxtfusion.layerwise_blocks.0.prenorm.scale.Lorlayer- index of current layerLCorlayer_count- number of layers
-
IMAGE KERNEL: in
conv/convolutionorezconv/ezconvolutionfunction kernel inputkX,kY,kZ(also lowercasekx,ky,kz) - position in kernel, centered at0.0;kZ/kzis only set for 3D kernelskW,kernel_width- kernel widthkH,kernel_height- kernel heightkD,kernel_depth- kernel depth
-
AUDIO:
Borbatch- batch indexNorchannel_count- channel countCorchannel- audio channelSorsample- current sampleTorsample_count- audio length in samplesRorsample_rate- sample rate
-
VIDEO
- refer to
IMAGE and LATENTfor the visual part batchmeansframebatch_countmeansframe_count- refer to
AUDIOfor sound part
- refer to
-
NOISE
- refer to
IMAGE and LATENTfor most variables Iorinput_latent- latent used as input to generate noise
- refer to
-
GUIDER
- refer to
IMAGE and LATENT sigma- current sigma valueseed- seed used for noise generationsteps- total number of sampling stepscurrent_step- current step index,0..stepssample- tensor input to guider or output from sampling
- refer to
SelectiveGuiderMathNode (hooking)
Adds support for hooking into specific layers/blocks of the model during guided diffusion.
Current implementation details:
hook_targetsupports runtime filtering in node UI.layer_xis used as direct index match (idx == layer_x) for current hook context.- For guiders with
original_conds, base conditions are restored before reattaching hooks to avoid hook accumulation across reruns/interrupted runs. - Active hook paths currently include:
- Attention override (
attn1/attn2/double_block_attn/single_block_attn/attn_unknown) - DiT block replace (
dit.double_block,dit.single_block) - UNet block patches (
input_block_patch,middle_patch,output_block_patch) - Timestep embedding start via
emb_patch(block_name="time_emb",layer_x=0) - Whole-model edges via diffusion-model wrapper (
model_begin,model_end)
- Attention override (
Side behavior (positive / negative)
When guider has original_conds, hooks are attached separately for:
positivenegative
Runtime variables expose side information:
cond_side:"positive" | "negative" | "mixed" | "unknown"cond_index:0 | 1 | -1is_positive:1.0or0.0is_negative:1.0or0.0
Variable reference (SelectiveGuiderMathNode)
The following variables are available in Expression.
| Variable | Type | Description |
|---|---|---|
inp |
tensor | Current tensor received by the active hook. |
sample |
tensor | Alias of inp. |
F0..Fn |
float/tensor | Individual float inputs from the node. |
F |
list/tensor | Collection of all float inputs. |
D0..Dn |
tensor | Per-dimension index tensors from generate_dim_variables. |
S0..Sn |
float | Per-dimension sizes from generate_dim_variables. |
hook_kind |
string | Active hook identifier: attn1, attn2, double_block_attn, single_block_attn, attn_unknown, dit_block, unet_block, model_begin, model_end, or unknown. |
hook_domain |
string | High-level domain: attention, diffusion, or unknown. |
attn_kind |
string | Attention kind: attn1, attn2, double_block_attn, single_block_attn, attn_unknown, or none outside attention hooks. |
transformer_index |
float | Attention sub-block index inside a UNet block (-1 if unavailable). |
is_attn1 |
float (0/1) | 1 when current hook is attn1, else 0. |
is_attn2 |
float (0/1) | 1 when current hook is attn2, else 0. |
is_attn1_hook |
float (0/1) | 1 when attn_kind=="attn1", else 0. |
is_attn2_hook |
float (0/1) | 1 when attn_kind=="attn2", else 0. |
is_dit |
float (0/1) | 1 when current hook is DiT block hook, else 0. |
is_unet_block |
float (0/1) | 1 when current hook is UNet block hook, else 0. |
is_time_emb |
float (0/1) | 1 when current hook is timestep embedding entry (block_name=="time_emb"), else 0. |
block_name |
string | Block/stage name (input, middle, output, time_emb, model, DiT block type, etc.). |
layer_id |
float | Numeric block/layer id used by current hook context. |
layer |
float | Alias of layer_id. |
i |
float | Alias of layer_id. |
layer_key |
string | Composite identifier for debug/filtering (for example output.6.attn2.0, unet.time_emb.0, model.begin). |
total_blocks |
float | Total blocks in stream if available, otherwise -1. |
has_qkv |
float (0/1) | 1 in attention hooks where q/k/v are valid; 0 in diffusion/block hooks. |
q |
tensor | Query tensor in attention hooks; fallback placeholder otherwise. |
k |
tensor | Key tensor in attention hooks; fallback placeholder otherwise. |
v |
tensor | Value tensor in attention hooks; fallback placeholder otherwise. |
heads |
float | Number of attention heads (attention hooks only, else 0). |
dim_head |
float | Per-head channel size (q.shape[-1] / heads) when available. |
activations_shape |
list | Raw shape from transformer context. Empty list if unavailable. |
activation_b |
float | Batch dimension from activations_shape[0] (or -1). |
activation_c |
float | Channel dimension from activations_shape[1] (or -1). |
activation_h |
float | Height dimension from activations_shape[2] (or -1). |
activation_w |
float | Width dimension from activations_shape[3] (or -1). |
attn_mode |
string | Legacy compatibility field (default unknown). |
attention_relation |
string | Inferred semantic relation: self, cross, or unknown. |
is_self_attention |
float (0/1) | 1 when the active attention is self-attention. |
is_cross_attention |
float (0/1) | 1 when the active attention is cross-attention. |
has_context |
float (0/1) | 1 when attention context appears to be present. |
query_tokens |
float | Query sequence length. |
context_tokens |
float | Context sequence length. |
value_tokens |
float | Value sequence length. |
activation_rank |
float | Rank of activations_shape. |
activation_t |
float | Temporal dimension for video-like activations (-1 if unavailable). |
Practical notes
-
On SD1.x, repeated hits on the same
layer_idare normal in attention because one UNet block can contain multiple transformer sub-blocks. -
Use
transformer_indexto target exactly one sub-block. -
For timestep-begin hooking use
layer_x=0and filter byblock_name=="time_emb"(orlayer_key=="unet.time_emb.0"). -
For model-edge hooks filter by
hook_kind=="model_begin"orhook_kind=="model_end". -
For model-agnostic expressions, prefer guard variables:
has_qkv,is_dit,is_unet_block,is_attn1,is_attn2. -
attn2is only a hook label, not guaranteed to mean real cross-attention. -
Use
is_cross_attentiononly as an inferred relation from runtime metadata. -
Use
attention_relationfor semantic relation (self/cross) andattn_kindfor hook-path classification. -
Selective guider math:
hook_target:all,dit_block,unet_block,attn1,attn2,double_block_attn,single_block_attn,model_begin,model_end