Run JS
Automate your whole ComfyUI workflow with a little JavaScript
ComfyUI is a programming language with no keyboard - switches, reroutes, and a hundred plumbing nodes let you encode decisions into the graph itself. RunJS (from the comfyui-run-js pack) is the logical endpoint of that idea: a node whose whole job is to run arbitrary JavaScript against your workflow, from inside the workflow. Set it to fire on an event, paste a script into the text box, and it will read every node in the graph, rewrite widget values, bypass or create nodes, and even queue generations - then sit quiet until the next trigger.
It's not the node you need every day. For "randomize the seed each run," the built-in control_after_generate widget already does that, and nobody should install a scripting node for it. Reach for RunJS when the built-in controls stop being enough: sweep a prompt through twenty variants, run a cartesian grid of samplers × schedulers, cancel a queue that's backed up past five jobs, toggle a detailer on and off mid-session. It's a power-tool node with zero search impressions and basically no Reddit footprint, but it's real and it works.
How it works
Here's the surprising part: the Python side of RunJS is a stub. The node class just declares its widgets so the node exists in your graph; all the machinery lives in a frontend extension that hooks ComfyUI's API events. When the event you picked fires, the extension takes whatever's in the text widget and evals it as an async function in your browser tab, with a pile of pre-bound globals: NODES, GROUPS, LINKS, SELF, and two persistence stores - SESSION (lives until you close the tab) and PROPS (written to the node's properties, so it survives reload and workflow save). Helpers do the heavy lifting. Node("KSampler") finds a node by title, type, or id; setValues(node, {...}) writes widgets; generateSeed() rolls a safe seed; run() queues a prompt; showInfo() toasts a message. await works, so you can await run() inside a loop.
The README's other trick is worth stealing: the pack ships a SKILL.md designed to be fed to an LLM. Give an AI the file's URL plus "randomize the seed on KSampler, set steps to a random value between 10 and 30, then generate," and it writes a working script for you to paste in. That's the intended onboarding path, and it works surprisingly well - handy when the API surface (node lookup, connect, bypass, queue control) is more than you want to memorize.
The inputs that matter
event- which trigger runs the script. The ones you'll actually use:before_queued(right before a prompt queues - the seed-randomizer slot),executed/execution_success(after work completes),progress,comfyui_setup(once, after the frontend loads),workflow_changed, andNone(never fires on its own - the script only runs when you click the node's Run button). The rest of the enum exists for niche automation.text- the JavaScript itself, multiline. This is the whole node.
It has no outputs. RunJS doesn't pass data anywhere; it acts on the graph and queues jobs. Wire nothing out of it.
Installing it
ComfyUI Manager → search "comfyui-run-js", or:
cd ComfyUI/custom_nodes
git clone https://github.com/shinich39/comfyui-run-js
Then restart ComfyUI. There are no Python dependencies, no model downloads, no requirements.txt - it's pure frontend, so a browser refresh is often enough after an update. From the graph: Add Node → utils → Run JS.
Gotchas
- It runs in your browser, not the server. Close the tab and
before_queuednever fires. If you queue from the API or another client, the scripts silently don't run. - It's
evalon your workflow text. The pack itself is MIT, but scripts people paste run with full access to your graph. Treat a stranger's RunJS script like a stranger's custom node - the ecosystem's malware scar (ComfyUI_LLMVISION) is exactly this trust model. Only run scripts you understand. - Errors don't crash ComfyUI - they're caught, logged to the browser console, and shown as a red toast. If nothing happens, hit F12 and look.
- A bypassed RunJS node is skipped by every event listener. Also,
Node()returns the first match; useNodes()for multiples.
It's a niche tool, and honestly that's fine - it earns its place the moment you need a grid search ComfyUI's widgets can't express. Start with the seed-randomizer and let the AI writer handle the rest.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| event | COMBO | 15 options: None, before_queued, after_queued, status, progress, executing, +9 | |
| text | STRING | — |
Outputs (0)
No outputs