comfy-math-strings

v2026.09.24

ComfyUI compute/string nodes: constants, sliders, math expressions, string concat/split/replace/regex, type conversion, JSON/list utilities. Use when computing a value or assembling a string in a workflow.

GitHub
Install command
npx skhub add laurigates/comfy-math-strings
Markdown
SKILL.md

ComfyUI math & strings

Compute and assemble. Numbers in, numbers out; strings in, strings out; primitives that hold a constant; sliders that expose a tunable.

The split:

PackNiche
comfyui-kjnodesPrimitives (BOOL / INT / Float / String / Multiline), SimpleCalculatorKJ, JoinStrings / JoinStringMulti, AppendStringsToList, Something/WidgetToString
comfyui_essentialsMath hierarchy: SimpleMath / SimpleMathFloat / SimpleMathInt / SimpleMathDual (AST-based)
comfyui-custom-scripts (pysssss)MathExpression (full numpy + math imports), StringFunction (regex), StringNodes (split/case/trim)
comfyui-mxtoolkitmxSeed, mxSlider, mxSlider2D widget-driven values
bjornulf_custom_nodesAnythingToText / AnythingToInt / AnythingToFloat, TextReplace, CombineTexts, RandomLineFromInput
ComfyUI-CrystoolsCBoolean / CInteger / CFloat / CText / CTextML primitives, CJsonFile / CJsonExtractor / CListAny / CListString
comfyui-easy-useeasy string / easy int / easy float identity nodes, easy rangeInt
comfyui-dream-projectDreamCalculation, DreamLinear, sine/saw/triangle waves (animation-flavored, see "out of scope")
comfyui_yvann-nodesFloatToInt, InvertFloats, MaskToFloat, FloatsToWeightsStrategy

When to Use This Skill

Use this skill when...Use instead when...
Computing a value or assembling a string in a workflowForming a boolean from that computation -> comfy-conditionals
Converting between types (anything -> int/float/string)Displaying the result -> comfy-debug-preview

Sources of truth

  • custom_nodes/comfyui-kjnodes/nodes/nodes.py — primitives, JoinStrings, Something/WidgetToString
  • custom_nodes/comfyui_essentials/misc.py — SimpleMath family
  • custom_nodes/comfyui-custom-scripts/py/math_expression.py — pysssss MathExpression
  • custom_nodes/comfyui-mxtoolkit/ — slider widgets
  • custom_nodes/ComfyUI-Crystools/crystools/nodes_*.py — primitives, list helpers, JSON extractor
  • custom_nodes/bjornulf_custom_nodes/ — Anything-To-* converters

Math — which node?

NeedBest nodeWhy
Single FLOAT expression, simple opsSimpleMathFloat (essentials)Tight; one expression in, FLOAT out
Single INT expressionSimpleMathIntSame, INT result
Multi-variable expression (a, b, c, d) — (a+b)/c etc.SimpleMathAST-based eval over named inputs; safe (no module imports)
Two values, binary op (add/sub/mul/div/pow)SimpleMathDualOp picker widget
Many variables (a–j)SimpleCalculatorKJ (kjnodes)Up to 10 named inputs
Need numpy / math import — np.sqrt, math.pi, np.clip, etc.MathExpression (pysssss)Full module access, expression as string
Animation curve over a frame index — linear ramp, sine waveDreamLinear, DreamSineWave etc.Built-in time/frame parameter
Float ↔ INTyvann FloatToInt (round/ceil/floor), AnythingToInt (bjornulf)yvann has the rounding mode; bjornulf is the lenient any-input version
Invert FLOAT (1−x or −x)yvann InvertFloatsMode picker

Math hierarchy in one line

SimpleMathFloat < SimpleMath < SimpleCalculatorKJ < MathExpression

Pick the simplest one that compiles. MathExpression (pysssss) is the "escape hatch" — full numpy + math imports, but a typo crashes the node with an opaque traceback. Use it only when AST-safe SimpleMath can't express what you need.

SimpleMath syntax

Variables: a, b, c, d (wire them as inputs). Operators: + - * / // % **, parentheses, unary -. Functions: min, max, abs, round, int, float.

SimpleMath:
   expression: int(sqrt(a * 1000000 * b) / 8) * 8
   a = megapixels
   b = aspect_ratio
   → width snapped to multiple of 8

SimpleMath does NOT support sqrt natively — drop to MathExpression for that. Actual AST-safe set is limited; check the node source for the allowed call list.

MathExpression syntax

import numpy as np
import math

# Expression — single line:
int(math.sqrt(a * 1e6 * b) / 8) * 8

# Or use numpy:
int(np.sqrt(a * 1e6 * b) / 8) * 8

You write an expression string referencing input variable names that the node has registered. The node has math and numpy (as np) already imported. Errors surface as red node + traceback in the server log; nothing in the workflow UI.

Strings — concat, replace, regex, split

NeedBest node
Concat a few strings with a delimiterJoinStrings (kjnodes, 5-arg)
Concat manyJoinStringMulti (kjnodes, dynamic N inputs)
Concat 2 with no delimiterCombineTexts (bjornulf)
Find/replace literalTextReplace (bjornulf)
Find/replace regexStringFunction (pysssss; regex_mode toggle)
Pick random line from multilineRandomLineFromInput (bjornulf)
Split on delimiterStringNodes (pysssss, operation picker) or TextSplitByDelimiter (mixlab)
Add line numbersAddLineNumbers (bjornulf)
TokenizeDreamStringTokenizer (dream-project, word-splitter)
Build STRING from a listAppendStringsToList (kjnodes)

Single-line vs multiline

  • StringConstant (kjnodes) — single line, no embedded newlines
  • StringConstantMultiline (kjnodes) — multiline, ideal for prompt bodies, JSON snippets, multi-clause text
  • CText (Crystools) — single line
  • CTextML (Crystools) — multiline

Pick multiline for any string longer than ~80 chars or any string with embedded newlines. Single-line variants are for short labels and filenames.

Regex via StringFunction

StringFunction (pysssss):
   action: replace
   regex_mode: ON
   text:    "michael.jpg"
   find:    "\.(png|jpg|jpeg|webp)$"
   replace: ""
   → "michael"

Useful for stripping extensions from a filename (the exact pattern we hit in the comfy-image-utils recipe set). Action picker also supports append, replace, tidy tags (the last one normalizes commas/spaces in tag lists for booru-style prompts).

Type conversion

bjornulf's AnythingTo* family is the most lenient — it accepts literally any input type and produces the named scalar:

NodeOutputNotes
AnythingToTextSTRINGstr(x)-equivalent on any input
AnythingToIntINTCoerces float / string / bool; raises if string isn't parseable
AnythingToFloatFLOATSame, FLOAT target
SomethingToString (kjnodes)STRINGSimilar to bjornulf's AnythingToText
WidgetToString (kjnodes)STRINGReads a specific widget by name from a target node — useful for surfacing arbitrary widget values into the data flow

easy string / easy int / easy float (easy-use) are identity nodes — they don't convert, just pass through. Their purpose is to expose a widget UI for a value that downstream nodes will consume; a debug-friendly handle.

Primitives & sliders

SourceWhat it gives you
kjnodes INTConstant / FloatConstant / StringConstant / BOOLConstant / StringConstantMultilinePlain widget-input constants
Crystools CInteger / CFloat / CText / CTextML / CBooleanSame, plus some have widget toggle for live update
mxtoolkit mxSliderSingle tunable INT or FLOAT slider with live drag
mxtoolkit mxSlider2D2D drag-pad emitting two independent values
mxtoolkit mxSeedINT pass-through with a seed-control widget (random / fixed / increment)
easy rangeIntEmit a range of INTs — start, end, plus step mode or num_steps mode
RepeatImageToCount is image-specific (covered in comfy-image-utils); for value-list repetition use Python via JoinString

Seed strategy

ComfyUI core's Seed node and the seed widget on KSampler both support fixed/random/increment. mxSeed adds a slider-style UI and a pass-through value (useful when one seed feeds multiple samplers and you want it visible). Seed Everywhere (cg-use-everywhere) is deprecated in favor of Anything Everywhere connected to an INT.

JSON & lists (Crystools)

NodeUse
CJsonFileLoad a JSON file from disk; emits the parsed structure as a JSON object
CJsonExtractorExtract values from a JSON object via dot-path / JSONPath syntax
CListAnyBuild / pass a list of any type
CListStringBuild a list of strings (sometimes more convenient than concatenation)

Typical use: load a config JSON, extract one value, feed into a downstream node. The Crystools JSON nodes don't do JSON-write; for that, save text via bjornulf SaveText or use the MathExpression escape hatch.

Recipes

Resolution math from megapixels + aspect ratio

You want a target image size of "≈1 MP, 16:9 aspect, both dimensions divisible by 8".

PrimitiveFloat (mp = 1.0) ──┐
                            ▼
PrimitiveFloat (ar = 16/9) ─►  MathExpression (pysssss)
                            ▲     expression: int(math.sqrt(a * 1e6 * b) / 8) * 8
                            │     a = mp, b = ar
                            ▼
                          width = 1336 (for ar=1.78)
                          (compute height by 1e6/width or as another expression)

Two MathExpression nodes: one for width, one for height = int((a * 1e6) / b / 8) * 8 with the same mp input and width as b. The 8-snapping handles SD/Flux/Wan latent alignment automatically.

Filename templating

SaveImage.filename_prefix accepts %date:yyyy-MM-dd% / %date:hhmmss% substitution plus %NodeName.widget%. A run-signature shape (sampler/scheduler/seed) keeps runs distinguishable, but the exact convention is per-install — see "Naming conventions are per-install" in comfy-workflow-json. When the native substitution isn't enough (e.g. you need to strip an extension from a source filename), assemble the prefix via:

LoadAndResizeImage ──► image_path (STRING, kjnodes)
                            │
                            ▼
                StringFunction (pysssss)
                   action: replace, regex: ON
                   find:    "\.(png|jpg|jpeg|webp)$"
                   replace: ""
                            │
                            ▼ (basename without ext, STRING)
              JoinStringMulti (kjnodes)
                 in_1: "<bucket>/%date:yyyy-MM-dd%/%date:hhmmss%_%ksampler.sampler_name%_%ksampler.scheduler%_s%ksampler.seed%_"
                 in_2: <stripped basename>   # the <descriptor> segment
                            │
                            ▼
              easy imageSave (filename_prefix STRING input)

The %date:...% tokens are passed through verbatim; SaveImage's internal substitution resolves them at save time. The %LoadImage.image% widget-substitution is bypassed entirely — we build the final string in the graph.

Build a comma-separated tag list from individual triggers

Three LoRA trigger words plus a manual prompt, combined:

LoraLoaderVanilla (lora_1) ──► civitai_tags_list (STRING)  ──┐
LoraLoaderVanilla (lora_2) ──► civitai_tags_list (STRING)  ──┤
LoraLoaderVanilla (lora_3) ──► civitai_tags_list (STRING)  ──┤
PrimitiveStringMultiline ("a portrait of a woman") ───────────┤
                                                              ▼
                                              JoinStringMulti (delimiter = ", ")
                                                              │
                                                              ▼
                                                       CLIPTextEncode

Empty trigger strings produce an extra , — pipe through StringFunction (action: tidy tags) to collapse redundant separators.

Range-driven batch

Generate 10 images at incrementing CFG values:

easy rangeInt
   start: 3,  end: 12,  num_steps: 10
        │
        ▼ (emits 10 INTs at execution)
   (route into a forLoopStart, each iteration sets KSampler.cfg)

Pair with easy forLoopStart / forLoopEnd (see comfy-flow-control) for actual iteration.

Gotchas

  • SimpleMath ≠ MathExpression. Essentials' SimpleMath is AST-safe (no imports, limited function set — no sqrt, no numpy). Pysssss MathExpression has full math + numpy access. Don't mix them up.
  • kjnodes Constants vs Crystools Constants — they look interchangeable. They mostly are, but Crystools' CFloat / CInteger widgets default to wider ranges and Crystools' multiline CTextML has different newline handling on Windows. Pick one pack per workflow for consistency.
  • MathExpression errors are server-side. The node turns red but the error message ("Error executing MathExpression: NameError") lives in the ComfyUI server log (journalctl -u comfyui.service on this install). Add a ShowAnything on the output to confirm it's emitting what you expect.
  • StringFunction regex syntax is Python re. Forward slashes are NOT escapes; backslashes are. \.(png|jpg|jpeg|webp)$ works. /\\.(png|jpg|jpeg|webp)$/ does not — that's JavaScript syntax.
  • AnythingToInt on a non-numeric string crashes. "3.14" → fails; "3" → works; 3.14 (float) → 3. To coerce a possibly non-numeric STRING, route through MathExpression with int(float(a) if a else 0) and a defensive try wrapper.
  • JoinStringMulti shrinks dynamic inputs. The first time you add a downstream consumer, the node grows its slot count. If you later disconnect, the empty slots stay around. Drop and re-add the node to compact.
  • easy rangeInt num_steps is INCLUSIVE on both ends, so start=0, end=10, num_steps=11 gives [0, 1, 2, ..., 10]. With num_steps=10, you get [0, 1.11, ..., 10] — not integer. Prefer the step mode when you want integer-only spacing.

Cross-refs

  • comfy-conditionals — feeding math output through a comparator to form a boolean predicate.
  • comfy-debug-preview — ShowAnything / DisplayAny / ShowFloat for inspecting intermediate math results.
  • comfy-flow-control — easy rangeInt feeding forLoopStart; primitives as switch selectors.
  • comfy-prompting — string assembly for prompts (this skill is the primitive layer; comfy-prompting is the application).
  • Project CLAUDE.md "SaveImage filename substitution" — native %date:%/%NodeName.widget% syntax that complements the string-graph approach.

Things this skill does NOT cover

  • Animation schedulers — DreamSineWave, DreamSawWave, DreamTriangleWave, FloatsToWeightsStrategy, frame-counter ops. These are math primitives but their application (AnimateDiff / IPAdapter transitions / weight schedules over frames) is its own domain. Future comfy-animation-schedules skill will cover this. For now: the nodes exist, they emit FLOATs / FLOAT lists driven by a frame index; consult the comfyui-dream-project and comfyui_yvann-nodes repos for application examples.
  • Display-only nodes — ShowText, DisplayAny, ShowFloat, Crystools CConsoleAny*. Those are inspection, not computation; see comfy-debug-preview.
  • Image / mask arithmetic — ImageBlend, MaskComposite. Those are pixel ops, not scalar math; see comfy-image-utils.
  • Sampler internals — cfg, denoise, step counts. Math-driven scheduling of those values is fine, but the values themselves live in the model-family skills.
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

comfyui-plugin/skills/comfy-math-strings

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3