Nodes/ꘇ Genereti/ꘇ top.expression
ComfyUI Node

ꘇ top.expression

Write a tiny shader in a text box and get an image out

By languel·Created about a month ago·Updated 3 days ago· 1
ꘇ top.expression
  • image
  • controls
  • image
◄expression0.5 + 0.5*sin(t + x*12)*cos(y*12)►
◄width512►
◄height512►
◄time0.00►
◄performance{}►
◄offset_x0.00►
◄offset_y0.00►
◄offset_z0.00►
◄offset_t0.00►
◄parameters{}►

This is the one node in the pack that feels like a different category of tool. top.expression evaluates a math expression per pixel, per channel, so you can build a procedural texture, drive a value from the clock, or do arithmetic on an input image without ever leaving the graph. Two lines of formula gets you a plasma field, a radial gradient, a scanline wipe, a ripple - the sort of thing you'd otherwise open After Effects for. The KB's read on this layer is that people leave ComfyUI for a compositor to do exactly this kind of trivial math; here's the node that keeps them in.

How it works

You write an expression in the multiline text box. It's evaluated for each of R, G, B in turn, with these variables in scope:

  • t seconds, x/y normalized 0..1 across the frame, i the pixel index, c the channel index (0,1,2)
  • v and a - the input value for that pixel and channel (identical; b is a constant zero, kept for symmetry)
  • w, h - dimensions; z for 3D patterns; pi and tau
  • __time, __beat, __bar, __bpm, __ticks, __phase, __playing, __rate, __root, __tuning - read from the performance JSON, so a musical transport can drive the pattern.

The default expression is 0.5 + 0.5*sin(t + x*12)*cos(y*12) - a plasma grate that animates. Functions available: sin cos tan abs sqrt floor ceil exp log min max minimum maximum pow fract clamp mix step, plus noise, perlin, simplex, value for coherent noise. ^ is accepted as a power operator. Results are clamped to 0..1 and non-finite values become zero rather than blowing up the graph.

It's a restricted evaluator, not Python: arithmetic, named variables and whitelisted functions only, expressions capped at 2048 characters and 512 syntax nodes. That's a safety boundary, and it also means no conditionals and no loops - you get one line of math.

If you connect an IMAGE, the resolution comes from the image and v/a are that pixel's actual value, so the node is a per-pixel filter. Alpha is copied from the source. If you leave the image socket empty, the node generates a texture at your width/height with alpha 1.

The other input worth knowing is controls. Drop a // @param comment into the expression and the node grows a wireable widget for it:

// @param radius = 0.35 (0.0..1.0)
// @param speed = 1.0 (0.0..4.0)
step(radius, x) * (0.5 + 0.5*sin(y*6 - t*speed))

There's no if and no comparison operators in the whitelist - which is exactly why step and clamp are there. Params become autogrow inputs named value0, value1… in declaration order, so you can wire anything numeric - a chop signal, another node's output - straight into the formula. parameters is a socketless JSON string holding their current values, mainly useful when a workflow is being driven programmatically.

Install

ComfyUI Manager → search ꘇ Genereti → install, or:

git clone https://github.com/languel/genereti.git custom_nodes/genereti
./.venv/bin/python -m pip install -r custom_nodes/genereti/requirements.txt

Restart Comfy, refresh the browser. Requirements are a single frontend pin; no models to download. Look for ꘇ top.expression under Genereti / TOP, and search genereti - the leading mark is a glyph and it sorts oddly in the node list.

Traps

In Queue it's NumPy on the CPU. Live mode evaluates on the browser GPU; a Queue run walks the pixels in Python. At 512×512 that's fine, at 4K on a batch it is not. Generate small and scale up with top.cleanup.

Live t and Queue t are different clocks. In Live, t is the time widget plus the running graph clock, so the pattern animates while you watch. In Queue, t is just the time widget - freeze or animate it deliberately.

Errors are silent-ish. Out-of-range variables raise "Unknown variable"; non-finite results are quietly clamped to zero. If a region of your output is black, check your division.

The pack is labelled super-alpha / early classroom testing; back up workflows before updating.

CategoryGenereti / TOP

Inputs (12)

NameTypeDefaultDescription
expressionSTRING0.5 + 0.5*sin(t + x*12)*cos(y*12)—
widthINT5121–4096—
heightINT5121–4096—
timeFLOAT0.00-100000–100000—
imageoptIMAGE—
performanceoptSTRING{}—
offset_xoptFLOAT0.00-100000–100000Domain offset x; zero leaves the domain unchanged
offset_yoptFLOAT0.00-100000–100000Domain offset y; zero leaves the domain unchanged
offset_zoptFLOAT0.00-100000–100000Domain offset z; zero leaves the domain unchanged
offset_toptFLOAT0.00-100000–100000Domain offset t; zero leaves the domain unchanged
parametersoptSTRING{}—
controlsoptCOMFY_AUTOGROW_V3—

Outputs (1)

NameTypeDescription
imageIMAGE—