ꘇ dat.expression
Do maths across every cell in a table
- input
- table
- text
ꘇ dat.expression runs one small arithmetic expression over every numeric cell in a table and gives you the result as a new table. Default expression is v * 2, which is a fair summary of the node: transform a grid of numbers without writing a Python node for it.
What it is
The pack's operator families share one tiny arithmetic language - the same parser serves the texture and signal expression nodes - and the DAT version is the one that operates on grids. That makes it the tool for the jobs a table is good at: scaling a CSV of parameters into range, thresholding a sampled image into a mask-ish grid, flipping or offsetting a matrix, or normalizing values you pulled in from somewhere else before they hit a texture node.
The alternative in stock ComfyUI is a custom Python node or a chain of per-value cells, and the KB's ecosystem notes are pretty clear on why that matters: nodes that hold a value or reshape data are the quiet majority of any graph, and packs that do this kind of plumbing well are the difference between a readable workflow and a wall of numbers. This is plumbing with an expression box on it.
How it works
For each cell, the expression is evaluated with a set of variables in scope. v and a are the cell's numeric value - non-numeric cells are treated as zero, so a text column turns into a column of zeros. x is the column index and y the row index, c is also the column index (the expression language reuses c for "channel/column"), i is the flat index y * width + x, w is the column count and h the row count. t and z are present and always 0 - there is no time axis for a table. On top of those you get the project fields (beat, bar, bpm, phase and friends) as __-prefixed values, which is why the node has an optional performance string.
The language is deliberately tiny: numbers, + - * / % ^, parentheses, pi, tau, and functions sin, cos, tan, abs, sqrt, floor, ceil, exp, log, fract, min, max, pow, clamp, mix, step. That's the complete list from the author's docs. No loops, no string functions, no property access, no conditionals - and no arbitrary JavaScript. It's a bounded arithmetic parser, and if you want a if you write step(0.5, v) style maths instead.
The output is a new table of numbers, plus the same content as CSV text. Two consequences of "numbers only" worth knowing before you point this at something: a name,value table comes out with the name column zeroed, and every cell is re-serialized as a number, so formatting is gone. This is a numeric transform, not a text editor - for text, use ꘇ dat.replace.
The inputs and outputs that matter
- input - the
GENERETI_DATtable to transform. - expression - STRING, multiline, default
v * 2. Usevfor the cell,x/yfor position,w/hfor the table's shape.x / (w - 1)is a handy normalization;sin(x * 0.5) * 0.5 + 0.5makes an interesting column-wise ripple. - performance - an optional STRING holding the project-time snapshot the expression reads from. Default
{}. In the live browser it's filled in automatically; you'd only set it explicitly for scripted runs. - table out - the transformed
GENERETI_DAT, each cell a number. - text out - the same table as CSV.
Note there's no rate or sample-count control here: tables recompute when their input changes, and the author's performance notes cap DAT recalculation at about 10 Hz. Small documents are the design intent; the docs literally say to keep DAT documents out of per-pixel paths.
Install
Everything here ships in the genereti pack. Manager → search genereti (title "ꘇ Genereti") → install → restart ComfyUI → hard-refresh the browser tab. Manual:
cd ComfyUI
git clone https://github.com/languel/genereti.git custom_nodes/genereti
./.venv/bin/python -m pip install -r custom_nodes/genereti/requirements.txt
Install with your ComfyUI's Python - the requirements file is just comfyui-frontend-package>=1.53.6. No models, no separate server, no dependency risk to your other packs. The bundled ꘇ-OpenTouch-Operators-and-Lessons workflow exercises the table family, and ꘇ-Inspect-Values is the quickest way to see a table before and after an expression.
Common issues
Your text column became zeros. Expected: non-numeric cells evaluate as v = 0. Transforms are numeric-only, so do text work with ꘇ dat.replace or ꘇ dat.select instead.
A syntax error kills the whole evaluation. The parser is strict - an unbalanced parenthesis or a function name that isn't in the list stops the node rather than skipping cells. Start from the default and add one term at a time.
Position variables can be confusing. x and c are the same number in DAT context, and i is the flat cell index, not a time. If your expression seems to do nothing per-cell, check you're not accidentally using t (always 0 here) as your variable.
It's not fast, and it isn't meant to be. Tables recalculate at up to 10 Hz and every cell is evaluated individually. A 512-row table is fine; a per-pixel grid is the wrong shape of work.
Alpha. The README's framing applies to this node as much as any: super alpha, early classroom testing, back up workflows before updating.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| input | GENERETI_DAT | — | |
| expression | STRING | v * 2 | — |
| performanceopt | STRING | {} | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| table | GENERETI_DAT | — |
| text | STRING | — |