WextraUI
Extra nodes for ComfyUI, from the Winteraction workbench. Time works for you: WCheckpoint, WLoRA, WLoRA Stack, WSampler, WScheduler, WInt (seed), WFloat (cfg or denoise) and WSwitch have a control after generate and the carry that links them, so a queue of runs changes everything on its own, and WSave Image writes into every file name the values that made that image. One press of Run and the machine never stops. 34 nodes in all, for the classic canvas and for Nodes 2.0. With them WReso, WRatio, WFrame, WPrompt Rows, WRoute, WRoute Index, Wif, WIndex and WDifference; under WextraUI/H3 the 7 of the scene loop for MiniMax H3 Sync Sound, a film as a list of scenes with continuous audio; under Video, Audio and Foley WExtractor, WMount, WCutScene, WAudio Prompts, the Foley loop (LTX-2.3 video-to-audio, a prompt per scene, in one Run), WMixer to build the soundtrack in the timeline inside the node and WAonV, the sound on the picture. No ffmpeg to install.
Nodes (2)
Time works for you.
WextraUI dresses ComfyUI as a production machine that never stops, and we mean it literally. A collection of custom nodes born from everyday needs.
In a world that runs hyper-fast, ComfyUI is still young as we write this (2026). Yet it does not seem to feel the friction of time: technical evolution and the open-source community make it take huge steps forward in a short while. What sometimes frustrated those who, like us, like to know how every single gear works, was not being able to automate certain trials: generating and comparing many variations by model, LoRA, seed, steps, cfg, but also sampler or scheduler. Until yesterday, changing any one of these parameters (seed apart) meant one or more separate runs.
The respective WextraUI utility nodes have a control after generate like the classic one of the seed, and by chaining their carry backwards, when the first node finishes its list it moves the second by one, like the digits of an analogue counter. So a grid of scheduler × sampler × seed × LoRA × checkpoint can be queued (machine permitting) with one press of Run. And our WSave Image can write into the name of every file the values that produced it, so you never reopen anything to see what you had tried, or, thanks to WDifference, the differences from the previous run.
It is not new magic, it is the same magic as ComfyUI: only the time of repetitive trials given back to whoever uses it, to spend looking at the results and making wonders.
There are other nodes in the pack, like the ones that build the sequence of scenes with continuous audio for MiniMax H3. Every node is documented below, one by one, and every node has a ? with its help page, readable right on the graph.
We hope they are as useful to you as they are to us. And by us we mean me and the AI (frontier or open-source), which can drive these nodes like any other in ComfyUI, through CLI or MCP.
Time is no tyrant. You only need to make it work for you.
Install
ComfyUI Manager: search WextraUI. Or clone it into your custom nodes:
cd ComfyUI/custom_nodes
git clone https://github.com/sickbraintwo/WextraUI
No extra dependencies. Restart ComfyUI; the nodes are in the WextraUI category, with four sub-menus: WextraUI/H3 (the seven of the H3 scene loop), WextraUI/Video, WextraUI/Audio and WextraUI/Foley.
<p align="center"><img src="images/help_popup.png" width="860" alt="WLoRA with its ? help page open"></p> <p align="center"><sub>Every node has a <b>?</b> in its title bar: the full help page, inside ComfyUI. This README is the tour, the <b>?</b> is the manual.</sub></p>The nodes work on the classic canvas (the default) and with the Nodes 2.0 renderer (Comfy.VueNodes.Enabled): the ?, the buttons and bars, the pills of WSwitch, the + chips and the row bars of WPrompt Rows are there in both. Switching Nodes 2.0 off gives every WextraUI node its classic size back (Nodes 2.0 draws taller rows and writes that height into the node). The pictures below are from the classic canvas.
The nodes
| Utilities | | H3 scene loop | | |---|---|---|---| | WSave Image | the file name says what made the image | WSimple Prompt H3 | prompt without time-codes | | WPrompt Rows | the prompt as rows you switch on and off | WScene Composer H3 | storyboard → time-coded prompt | | WRoute | the if that picks an output | WScene H3 | one scene as one object | | WRoute Index | same, by number | WScenes Collection H3 | the scenes, in order | | Wif | the if that picks an input, up to 20 pairs on one boolean, lazy | | | | WIndex | same, by number | | | | WDifference | what changed since the last run | WLoop Start H3 | which scenes this Run renders | | WLoRA | LoRA + trigger words, in line | WLoop Scene Conditioning H3 | the loop body | | WLoRA Stack | up to twenty LoRAs loaded together, blocks that fold | WLoop End H3 | the hand-off to the next scene | | WFrame | crop · resize · place, for outpaint | | | | WReso | read, define or multiply a size | | | | WRatio | the ratio of a size, its two numbers out | | | | WFloat | a float that walks, run after run | | | | WInt🌱 | an integer that walks: the seeds of a grid, a count | | | | WSwitch | slots of cables, one on, the rest asleep | | | | WCheckpoint | Load Checkpoint + folder + control after generate | | | | WSampler | the sampler menu + control after generate | | | | WScheduler | the scheduler menu + control after generate | | | | carry | the cable that makes a grid of two walks | | |
| Video | | Audio · Foley | | |---|---|---|---| | WExtractor | one frame of a video, or every frame between two | WMixer | a timeline mixer inside the node, played live | | WMount | videos joined into one mp4, every joint its own way | WAudio Prompts | the sound prompt of every scene, written by a vision model | | WCutScene | where the shot changes, checked by eye | WLoop Start Foley | the sound of a whole video in one Run | | WAonV | the sound on a video, the picture untouched | WLoop Window Foley | one window of the plan | | | | WLoop End Foley | the window into the mix, with its fades |
Utilities
Eighteen nodes for any workflow, image or video, whatever the model: the ones that walk run after run (checkpoint, LoRA, sampler, scheduler, seed, float, switch, frame), the carry cable that makes a grid of them, and the ones that read (WSave Image names the file after what made it, WDifference says what changed, WReso shows the resolution, WRatio its ratio). Menu WextraUI. Collapse a node that holds a value (WCheckpoint, WLoRA, WSampler, WScheduler, WInt🌱, WFloat, WSwitch, WReso, WRatio) and its title reads the name and the value: WSampler · euler, WReso · 1920 × 1080; open it again and the name is alone.
WSave Image
Saves the PNGs and writes into the name of every file what made it. The name is composed from parts: folder, subject, any string or number from the graph, a counter of chosen width; the usual ComfyUI metadata go inside. The node shows the name as it goes on disk before you run, linked fields included, and the name is the editor: click a part (each part in a dashed box) to pick it, blue, and open it below in its three rows text / type / value, with their sockets for a cable; click it again to shut it. The rows folder and subject stay under the name. images is optional: unplugged, the node only composes the name for other savers (video, JSON). The images output passes the batch through, so the save sits inside the chain instead of at a dead end.
A part not open is one row: the handle ≡ (drag it onto another part to move it), one box text · type · value (click the text or the value to type it, the type to turn it int → float → bool → string), − removes the part, + opens an empty one right below; values and cables follow. A field on a cable keeps its socket on the row and says the node it comes from. Under the name three chips: the counter ‹ _01 › (0 to 8 digits, 0 = none), batch, which appends _B{batch}{index}: how many images the run made and which one this is (_B41, _B42…; the same placeholders work in any text field), and thumbnails, the saved images inside the node.
{#id} in any text is the value that WextraUI node used in this run (WSampler, WScheduler, WCheckpoint, WLoRA, WFloat, WInt🌱, WSwitch), no cable. from Wnodes on graph lists the nodes of the workflow by type, the ones that walk already ticked, in the order of the carry chain (drag to change it), and makes one part for each (_sa_euler in the name, _C_ for a WCheckpoint, _L_ for a WLoRA, _S_ for a WInt🌱 on a seed, _st_ on steps, _cfg_ for a WFloat on a cfg; value locked to the node). A grid of trials, and every file telling on its own what made it.
<img src="images/WSaveImage.png" width="300" alt="WSave Image"> <img src="images/WSaveImage_fromWnodes.png" width="300" alt="WSave Image: from Wnodes on graph">
WPrompt Rows
The prompt as rows. Each one is a text area that grows with the text, with an on/off chip and a socket on the left for an external string (trigger words, another prompt) that the same chip switches. + adds a row, − removes one. The rows that are on come out joined by , , a space, a new line or nothing. Drag a row by its ≡ to move it; plug a text into prompt_to_divide and Divide Input Prompt turns it into rows, one per phrase between commas; while no row has text that text comes out as it is. Reset brings the node back to new.
WRoute · WRoute Index
The if the other way round: one input, two outputs, a boolean decides which output carries the value. The branch not chosen does not run at all, Save nodes included. WRoute Index does the same with a number: up to twenty outputs, counted from 0, shown as you use them. The index of WSwitch fits as it is. A line inside the node goes from value to the output it goes out of, red and dashed (with nothing runs under value) when that output has no cable.
<img src="images/WRoute.png" width="300" alt="WRoute"> <img src="images/WRouteIndex.png" width="300" alt="WRoute Index">
<details><summary>Why it exists, and how to join the branches again</summary>Nearly every switch in the ecosystem picks an input, because ComfyUI is pull-based; this one picks the output (ExecutionBlocker on the branch not taken). To join the branches back into one Save node use a lazy switch fed by the same boolean (Wif, core If/Else Switch, Easy-Use if-else). A non-lazy if (ComfyUI-Logic If) evaluates both inputs, meets the blocker and is skipped with everything after it: no error, no file.
Wif · WIndex
The mirror of WRoute: a boolean decides which input goes out, as Easy-Use If else; count (1 to 20) gives it as many true / false pairs, each with its own output, all driven by the same if. The inputs are lazy: only the branch chosen runs. WIndex does the same with a number, as Easy-Use Any Index Switch: up to twenty inputs, counted from 0, shown as you use them. A line inside the node goes from each input chosen to its output, red and dashed (with nothing runs under the output) when that input has no cable: nothing downstream runs, no error. Both are born as narrow as they can.
<img src="images/Wif.png" width="260" alt="Wif"> <img src="images/WIndex.png" width="260" alt="WIndex">
WDifference
Tells you what you changed since the previous run of this workflow: seeds, prompts, widget values, rewiring, nodes added or removed. As text, one line per change, and as a file-name-safe tag. Every node, or only the ones in track: select them on the canvas and press add (remove takes them out), each one becomes a chip with the node's title; ignore silences a node or a single widget (74 KSampler.seed). A week later the file still says what that run changed.
It reads the hidden PROMPT that every Save node embeds in the file, keeps the previous run per workflow on disk and returns the differences: ~ Composer I-a (#74) · seed: 2 → 6, rewiring, nodes added/removed. Optional .log = a run diary for free (output/_Wextra/rundiff/). changes (short, file-name safe: 74.seed→6_12.strength→0.6) or tag (246.strength=0.4_57.seed=77, first, same; display-only widgets ignored) as a string part in WSave Image, and the file name tells you what that run changed; the readable diff is in the .log.
WLoRA
Loads the LoRA and puts the trigger words you click into your prompt, in one node, in line: the prompt cable goes in on one side and comes out on the other with the words already in it. The words come from the <lora>.rgthree-info.json that rgthree-comfy saves next to a LoRA (the official words, rgthree_info switch) and from the training tags inside the file: click the chips, drag to reorder. They go in front of the prompt, with the separator between them (, to start; type \n for a new line).
Above the name, folder narrows the LoRA menu to one folder (any depth) and shows where you are in it (folder · 3/12 = runs to queue); under it, the selection drop-down of WSampler (all · 12, or tick the LoRAs you want, drag them into order, and it reads custom · 3: from then on the menu and the walk only meet those). Under the name, the seed-style control after generate (increment / decrement / randomize) walks the LoRAs of that folder, or of the selection, across queued runs. For a strength that walks, cable a WFloat on strength_model (and on strength_clip). The walk lives in the node, but folder is a real input (in object_info, optional), so a script reading the saved workflow sees the same values you see.
WLoRA Stack
Up to twenty LoRAs loaded together in one run, as twenty LoRA loaders in a row. One folder and one selection on top narrow the LoRA menu of every block (the LoRAs already chosen stay where they are); while the selection reads all, a button between them loads every LoRA of the folder into the blocks, up to twenty. Under them, step is how much the arrows move every strength (0.50 to start, two decimals); the control of each block (fixed, increment, decrement) walks its two strengths by that step after every queued run. Each block has the rows of WLoRA: the name, the two strengths, rgthree info, the chip picker. The words picked in the blocks go in front of your prompt, in the order of the blocks, with the separator between them (, to start; type \n for a new line). loras says how many blocks show and load.
The head row of a block has the handle ≡ (drag it onto another block: it takes that place and the ones between move, with their values, words and cables), the on / off switch (off: not loaded, its words left out), the block's number and the fold button: a click on the row folds the block without rgthree info and the tags, or down to one line with its name, strengths and words in columns. A block not folded to one line is a card, a shade darker than the node, and each block folds by itself. name gives the names of the blocks that are on, joined by +, for WSave Image. Under every block, solo walks one LoRA at a time: after every queued run only the next block is on (increment, decrement, randomize, increment-wrap), so five queued runs try five LoRAs one by one, while the blocks under a lock stay on in every run: ▶ 5 on the row of carry queues them from the first block, and the carry cable chains the stack with the other nodes that walk.
WCheckpoint
The core Load Checkpoint with the same three things as the LoRA loader. folder above the name: the folders of your checkpoint list at any depth, and the label of the name shows where you are (ckpt_name · 3/12 = runs to queue). The selection under it is a road: the checkpoint of now in the middle, the ones before and after it on the sides, a notch per checkpoint on the foot; open it and tick the checkpoints of that folder you want, drag them into order, and the menu and the walk only meet those. The seed-style control after generate under the name: increment / decrement / randomize walk the checkpoints of that folder, or of the selection, across queued runs. Same load as the core node (MODEL, CLIP, VAE, same caching), plus name as a string for WSave Image: one prompt, one seed, every model of a family in one go, and each file says which model made it. folder is a real input (optional, in object_info); a folder that is gone never stops a run.
WSampler · WScheduler
The sampler_name and scheduler menus as nodes of their own. Each has a selection row above the name that reads where the walk is: the name of now in the middle, the one before and the one after on its sides, the side the control goes to lit. A click opens the drop-down: tick the names you want, drag them into order, and from then on the menu, the arrows and the walk only meet those names and the seed-style control after generate under it. Turn the KSampler's sampler_name (or scheduler) into an input and cable the node into it: queue several runs with increment and each one takes the next name of the list. Both give the name as a string too, for WSave Image: one prompt, one seed, every sampler, and each file says what made it.
<img src="images/WSampler.png" width="280" alt="WSampler"> <img src="images/WScheduler.png" width="280" alt="WScheduler"> <img src="images/WScheduler_selection.png" width="280" alt="WScheduler: the selection drop-down">
WFrame
One node for the outpaint prep and every framing job: a stack of actions done in order, as many as the job needs. crop = the box that stays (a size or an aspect, an anchor, a shift); pad = the picture on a bigger (or smaller) canvas, what is missing padded with a colour, what sticks out cut; resize = aspect kept, by a side, a percent, or fit / cover a box. Crop a detail, then pad it to 1:1; resize, then pad for the outpaint. Out come the picture, the pad mask feathered inward (ready for outpaint) and the picture's width and height (with no action, those that came in); what was done goes in a WSave Image name with from Wnodes on graph (c800x800_p112x0). Under the stack, a preview: the final frame in red, every action a rectangle in the colour of its row with the row's number on it (full for the action opened last), the added border in the pad colour with its width on each side, what is cut outside; click a rectangle to open its action, drag it to move it, pull a corner to size it, nudge it with the arrow keys; a mask button shows the pad mask; the number boxes slide; in its bottom-right corner, what was done, as it goes in the file name. + crop, + pad, + resize add an action, the ✕ of a row removes it.
<img src="images/WFrame.png" width="260" alt="WFrame"> <img src="images/WFrame_mask.png" width="260" alt="WFrame: the mask view">
WReso
The size of a picture, in three sections on a switch of three, Read · Define · Multiply, one chosen at a time; the outputs (width, height) are the numbers of the section chosen, written large at the foot of the node. A click on the chosen section folds the node to the size alone. Every number of the node takes a cable.
- Read: plug a picture, or type a width and a height (or wire two INT cables: a resolution node, a WInt🌱, a Primitive), and read
1024 × 1024at a glance. The line follows the graph as you wire (a picture shown up the cable, the widget at the far end of a cable, a menu that reads1024 x 1024split in two); what the graph cannot tell reads?until the run, then the line shows what the node used. No convert-to-string, no concatenate, no Show node. - Define: one side and a ratio give the other side. Type the width or the height, pick the ratio (every landscape one in use, from
1:1to32:9: photo, screens, cinema; orcustomand two numbers of your own): a height of 832 at 16:9 reads1480 × 832. The side worked out is rounded to the nearest multiple of 8; while one side is above 0 the box of the other one is greyed out. invert turns the result round (832 × 1480): the upright frame.multipleis 1, 2, 4, 8, 16, 32 or 64, in a number box that takes a cable: a number in between goes to the nearest of them. - Multiply: a width and a height times a number with one decimal (
multiply by, 1.0 to start): 1024 × 576 at 1.5 reads1536 × 864, each side to the nearest pixel.
A WReso saved with 0.8.0 opens with Read and works as before; one saved on the Ratio Finding of 0.9.0 opens with Read too (that section is WRatio now). It runs with nothing on its outputs, like a Preview.
<img src="images/WReso.png" width="260" alt="WReso">WRatio
The ratio of a size, and its two numbers out. A width and a height, typed or by cable; their ratio written large, inside a rectangle of that ratio drawn to scale: 16:9 when it is exact, ≈ 16:9 when it is within 1%, the reduced fraction otherwise; small under it, the fraction and the decimal (185:104 | 1.78:1); on the right, the nearest sizes that are that ratio exactly with both sides multiples of 8, one bigger (↑) and one smaller (↓). Out, ratio_width and ratio_height: 16 and 9 for 16:9 or ≈ 16:9; a ratio with a decimal made whole, the same ratio (1.85:1 is 37 and 20). The two outputs fit the custom ratio of WReso as they are: the ratio of one picture, the size of another. A third output, aspect_ratio, fits WReso's ratio menu: 16:9, 9:16 for an upright picture (read upright, no invert), custom for a ratio not in the menu. It runs with nothing on its outputs, like a Preview.
WFloat
A float with a seed-style control: fixed, or increment / decrement by step after every queued run, with no arrival and no floor (negative values are legitimate). The row selection under value reads where the walk is: the value of now in the middle, the ones before and after on its sides (0.8 0.9 [1] › 1.1 1.2). For any FLOAT input: a strength through Set/Get, a denoise, a CFG. With a carry cable in or out it is a wheel, from the value you set to until; without a cable until does nothing and stays out of the view. Numbers that are not one after the other go in the selection under value: type them (0.5, 0.75, 1, 1.2), drag them into order, and the wheel goes through those instead, from the first one at the first run (on fixed, the first one).
WInt🌱
WFloat on an integer, with randomize too: a seed that walks by step (1 by default) after every queued run, for the KSampler's seed turned into an input or any INT. On increment-wrap it is a wheel from the value you set to until, round and round, no cable needed; with a carry cable the same wheel beats the next node: four seeds for every sampler, the seeds inside. Seeds that are not one after the other go in the selection under value: type them (1, 3, 6, 90, 1234), drag them into order, and the wheel goes through those instead, from the first one at the first run (on fixed, the first one). Drop a PNG saved by ComfyUI on the node and the seed that made it joins the list.
WSwitch
A switch with slots. A slot holds inputs_per_slot cables of any type (3 = model, clip, vae); the slot that is on goes out, position by position (out_1 = the first cable of the slot, out_2 the second…). One on/off pill per slot, next to its first cable: click one and the others go off. One slot is always on. Connect a cable to the last slot and an empty one appears below (up to 20).
The type of every position is learned from the first cable you connect there: the same position of every slot and the output take it, the socket gets its colour and refuses another type. The cables of the slots that are off are lazy: the branches behind them do not run (no VRAM, no seconds). The first output, index, is the slot that is on, counted like an array (0 = the first): cable it to whatever must follow the same choice (WRoute Index grows to match it). Set inputs_per_slot before wiring: changing it reshapes the slots and the cables of the positions that disappear are dropped.
WSwitch walks too: with control on increment the slot that is on moves to the next one in use after every queued run, and the carry cable drives it or is driven by it, like the other nodes that walk. Every slot is a box around its rows, the one that is on in blue, with its on/off pill, a pencil ✎ and a handle ≡: drag a slot by its handle onto another one and it takes that place, cables and name with it, nothing to unplug. The slots have names: click the pencil and type one (krea); a slot fed by a checkpoint, LoRA or UNet loader gets the bare file name by itself. The row reads 0 · krea, and in WSave Image {#id} of a WSwitch is the name of the slot that is on (its number when it has none): the short name of the model in the file name, with no String node and no Set / Get.
carry: the odometer cable
Every node that walks (WSampler, WScheduler, WCheckpoint, WLoRA, WLoRA Stack, WFloat, WInt🌱, WSwitch) has a carry input and output. Two of them, one inside the other: carry out of the fast one, into the slow one. Put the fast node on increment-wrap: each time it comes back from the last name to the first it sends one beat down the cable, and the slow node moves one step of its own (its control reads on carry; leave it on fixed). Like the digits of an analogue counter.
Chain as many as you like. WScheduler → WSampler → WCheckpoint is every checkpoint of a folder × every sampler × every scheduler of a selection, and the label of the input counts the runs: carry · 3 × 4 = 12 runs. Put 12 in the queue and the grid makes itself, with name of each node in the file name so every image says what made it. A WFloat on the cfg driven by the sampler: every sampler at 5, then every sampler at 5.5. A WInt🌱 with until as the first wheel: four seeds for every sampler.
The cable may pass through Set / Get and Reroute. It passes a string, and the backend, comfy-cli or an agent see an ordinary link: the beats happen in the interface, between one queued run and the next, where the walk already lives.
Two chips on the carry row of every node that walks, alone or in a chain. ⟲ puts every wheel of the chain back to its first place (the first seed picked, the start of the wheel, the first name of the selection or the folder, the first slot): a grid interrupted half way, or a new one from the start, with nothing to set by hand. ▶ 12 does the same and then queues the runs of the whole grid, 12 = the sizes of the chain multiplied; a node alone shows its own size (the seeds picked, the wheel to until, the folder or the selection, the slots in use). No number for a free walk by step (no arrival): then only ⟲. The number goes in the batch box next to Run too: the next Run queues the grid again, with nothing to type.
H3 scene loop
Seven nodes for one model: a film for MiniMax H3 Sync Sound as a list of scenes rendered one after the other, each clip joined to the previous one by a hand-off of frames and audio. You describe the scenes; the loop renders them, resumes from any point, and lets every scene choose how it joins the one before. Menu WextraUI/H3; the node types (h3Scene, h3LoopRange…) are the same as ever, saved workflows load as they are.
Each WScene H3 says how it joins the previous one: handoff_frames (last N frames + N/24 s of audio of the previous clip anchored at frame 0; valid H3 lengths 5/22/39/56…, 22 = 0.92 s default; longer = more music continuity across the join, more of the previous clip repeated), previous_from = loop (the clip rendered just before in the same run) or file + resume_from_video (scene-by-scene work: any clip as the previous one, scene 0 included, so a piece can start from the tail of any video). WLoop End H3 sits at the end of the loop body and cuts the tail the next scene asks for; WLoop Start H3 only chooses start_scene / scene_count. A run can therefore mix hand-off lengths per scene and resume anywhere.
WSimple Prompt H3
The H3 prompt without time-codes: what we see, what happens in order, then the things an H3 prompt must always carry. One camera idea (a still camera said in full), the sound born with the picture, what stays fixed, what moves, the final state, up to three things to avoid. Each one is a row with its name inside, as tall as its text. The order of the sentences is the timeline.
<img src="images/WSimplePrompt_H3.png" width="420" alt="WSimple Prompt H3">WScene Composer H3
Turns a storyboard into a MiniMax H3 prompt with a time-code per beat, so sound and picture are written together and land where you want them in the clip. Beats can be locked or scaled to the total duration; a bar of the clip shows them on their real seconds, and an edge of it drags. It also outputs the timing table and the real frame count.
<img src="images/WSceneComposer_H3.png" width="420" alt="WScene Composer H3">WScene H3
One scene as a single object: the prompt, its duration, the seed, where the clip starts and lands, an optional mid-clip guide, the references, and how it joins the previous scene. A strip on the node shows it all on the time of the scene.
<img src="images/WScene_H3.png" width="280" alt="WScene H3">WScenes Collection H3
Lines up the scenes in socket order into the list the loop runs over: the label of a socket is the number the loop gives that scene (0 · scene, 1 · scene…). That order is the film. The sockets grow as you connect, like the slots of WSwitch: unplug a scene in the middle and the ones after it move up; the + on a row opens an empty socket right below it, to slip a scene in between.
WLoop Start H3
Decides which scenes this Run renders and what the first of them starts from.
<img src="images/WLoopStart_H3.png" width="300" alt="WLoop Start H3">WLoop Scene Conditioning H3
The whole conditioning for one scene in one node: text, canvas, first/last frame, guide, references and the motion/audio hand-off from the previous scene.
<img src="images/WLoopSceneConditioning_H3.png" width="320" alt="WLoop Scene Conditioning H3">WLoop End H3
Closes one iteration of the loop: from the clip just rendered it cuts the tail the next scene asks for.
<img src="images/WLoopEnd_H3.png" width="300" alt="WLoop End H3">Video and sound
Nine nodes for the part of the job that comes after the generation: pick a frame out of a video, join clips into one film, find where the shots change, write a sound prompt for every scene, make the sound of the whole video with a video-to-audio model, mix tracks on a timeline, put the sound on the picture. No ffmpeg to install: everything goes through the PyAV that ComfyUI ships. Menus WextraUI/Video, WextraUI/Audio, WextraUI/Foley.
WExtractor
One frame of a video, or every frame between two. Pick the video (the input folder, then the output one, or upload one, or drag it onto the node), move the pointer on the timeline under the preview, a film of small frames of the video: the frame where you leave it goes out of images, with its number and its fps. Arrow keys for one frame (Shift: ten), space to play, F for the full screen. With multiple on, a second pointer, and every frame from the first to the second goes out, their number written between them (and how much memory they take when it is a lot). The frame picked is the frame that goes out: both are counted from the video's own timestamps.
WMount
Videos joined into one mp4: the clips of a folder of ComfyUI's input or output, in the order of their names, then the connected ones (a video or frames; the last socket connected makes a new one, up to 32). A strip shows the clips, one band each as long as its frames, what the rules take off hatched, the blends in blue, and under it the frames that will come out; a click on a band opens the rules of that clip: in, frames dropped from its head (22 by default, the frames a scene continued from the one before repeats); out, drop n frames from its tail (a loop that ends on its first picture: drop 1), or blend n frames into the first ones of the clip after, the sound fading across the same stretch. The frames go through one at a time, from the clips to the new file: a long 1080p mount does not fill the memory. The preview plays in the node.
WCutScene
Where the shot changes. The frames go in, the start of every scene comes out, one time per line, and on the node a sheet with the first, middle and last frame of each scene: a cut that was missed shows as two shots in one cell, a cut too many as the same shot in two. A cut is a sudden jump of the picture, much larger than the frames around it, so fast motion, a flash and a fade from black are not cuts; a cross-dissolve never jumps and is not found: write its time in the cuts box, where the found cuts are written after the Run and can be fixed by hand (add a line, delete one; empty the box to find them again). Under the boxes a strip shows the scenes in proportion to their length as you type, and says at once a time that is wrong. The beats box is for times where the shot goes on but the sound changes (a drop falls): written by hand, they make a scene of their own, marked +, which the Foley nodes fade in slowly instead of cutting.
WAudio Prompts
The sound prompt of every scene, written by Qwen3-VL (a Load CLIP, as Comfy's own Generate Text uses it: nothing outside ComfyUI) looking at a few frames of each scene, with the rules of a foley prompt built in: only sounds whose source is seen, material and timbre, what changes on screen, no invented events, no speech, no music. After WCutScene: its cuts say where each scene starts; out comes the timeline time | prompt for WLoop Start Foley. After the Run one row per scene shows the text that will go out: type in it to fix it, empty it to have just that scene written again, another seed for another text. mine? makes a scene yours and the model skips it. The two boxes behind the rows, prompts and custom, open with text, for pasting. On the node, each scene's middle frame with its prompt.
WLoop Start Foley
The sound of a whole video in one Run, a prompt for every scene, with LTX-2.3 and the Foley V2A LoRA on an Easy-Use For Loop. The video goes in whole with the timeline (time | prompt, from WAudio Prompts or by hand; | seed for a seed of your own; + before a time for a beat) and comes out as the plan: every scene cut into windows the model can take (window 169 frames = 7 s at 24 fps, the longest clip the LoRA was trained on), overlapping by overlap seconds inside a scene, meeting over cut_fade (0.1 s) across a cut, because a cut in the picture is a cut in the sound. windows is the loop's total, mix its initial_value1. redo_scenes (4 · 4, 7 · 4-6) with the previous_audio of an earlier run makes only those scenes again and puts them into the old sound, with the same fades.
WLoop Window Foley
Inside the loop: the window the loop's index points at. Its frames (the video's aspect at megapixels, 0.5 ≈ 960 × 544, the size the LoRA was trained at, sides to multiples of 32), how many they are (1+8n, for the audio latent), the prompt of its scene with the common text, its seed.
WLoop End Foley
At the end of the loop body: the sound just made goes into the mix at its place, with its fades, constant power so the level does not dip; at the last window the whole mix comes down under ceiling_db if it peaks above it (a volume change, nothing squashed). After the loop, mix is the sound of the whole video, 48 kHz stereo: save it as flac, put it on the picture with WAonV.
WMixer
A timeline mixer inside the node, as the layers of After Effects. Every track is an audio file of the input or output folder (or dragged onto the node), with its volume, offset, mute and solo; on each track the clips that play, each the room under its roof: the fades are its two slopes, the volume over time, with points and curves, is its top, and the waveform stands in that room. A map of the whole mix over the ruler says where you are when you zoom. It plays live while you edit (Web Audio), with the video next to it, muted, in step; the cuts of a WLoop Start Foley in the same graph are on the ruler, and the clips snap to them, to a beat grid (bpm, first beat) and to each other. Double-click makes a clip filling the scene, Ctrl+click cuts it, Alt+click deletes it; the mixer has its own undo. Space plays, F is the full screen, ? in the bar is the card with every gesture. Run makes the same mix in Python and gives it as AUDIO, under a ceiling.
WAonV
Audio on video: the sound on a video file without encoding the picture again, the sound the video had left out. The video from the menu (input, then output folder) or from a cable; the mix of WMixer or of the Foley loop, or a Load Audio; AAC at the bitrate you pick. length says whether the video, the sound or the shorter of the two sets the length of the file. The sound made on a small copy of the video goes on the high-definition one in seconds.
Made with it: RESONANCE
A ~77 s music video, video and audio generated together shot by shot with MiniMax H3, driven by these nodes and by an AI agent (Claude Code) working part of the loop through Comfy MCP: a world that exists only where sound touches it, five acts around one black riveted-iron skull. Nine WScene H3 chained end to end, all seeds fixed for reproduction. Entry for the Comfy H3 Sync Sound Community Challenge, September 2026.
- Watch it: RESONANCE on ArtStation · the page on the Lab: w-interaction.com/resonance.html (and wextraui.html for these nodes)
- Technical diary (pipeline, seed table, what H3's prompting will and won't do, the agent/MCP session):
workflows/RESONANCE.md - The workflow:
workflows/MM_H3_Loop_RESONANCE.json - Keyframes and references it loads (drop them in
ComfyUI/input):workflows/references/
The three workflows of the demo
Entry for Comfy's Open Source Higgsfield competition, November 2026: the demo on YouTube. The three workflows it shows are in workflows/challenge/, each with a note on the graph that says how to read it. The model, LoRA and file names in them are examples: pick your own from the menus.
Challenge_1_Reference_images.json: the carry on WCheckpoint, WLoRA, WSampler, WScheduler and WInt, one Run for every combination, WSave Image naming every file by what made it, WDifference on what changed.Challenge_2_Scenes_H3.json: the H3 scene loop that made RESONANCE, with its six stops written on the graph.Challenge_3_Sound_and_cut.json: WExtractor, WCutScene, WAudio Prompts, the Foley loop with LTX-2.3, then WMixer, WMount and WAonV.
Security
What the pack touches, for whoever reviews it:
- Files: only under
ComfyUI/output,ComfyUI/inputandComfyUI/temp(src/wxPaths.pyis the single gate for the first two: relative paths are resolved there, absolute paths are accepted only inside them,..is dropped; the video and audio nodes resolve their files the same way). WSave Image writes underoutput/, WDifference keeps its diaries inoutput/_Wextra/rundiff, WMount and WAonV write their mp4 underoutput/, WCutScene and WAudio Prompts their sheets undertemp/. WExtractor and WMixer read videos and audio files ofinput/andoutput/and keep a light copy (a small proxy, the sound as flac) undertemp/, made once per version of the file; a file dragged onto them is uploaded through ComfyUI's own/upload/image. WCheckpoint loads the checkpoint through the core node's code, by a name ComfyUI itself lists. WLoRA and WLoRA Stack read LoRA files, their header and — read-only, when there is one — the.rgthree-info.jsonnext to them, throughfolder_pathsonly, in the model folders ComfyUI itself lists; nothing is written back next to the LoRA. WAudio Prompts runs the vision model through ComfyUI's own CLIP loader: nothing is downloaded. - Network: none. The pack makes no outbound request of any kind. Its HTTP routes (
/wextraui/lora_tags,media_files,video_audio,video_proxy,extractor_info,extractor_files,mount_clips,mount_folders), all GET and served by ComfyUI itself, answer only about files in the folders above, from data already on disk. - Code: no
eval/exec, nosubprocess, no runtimepip, nothing obfuscated; the video and audio work is done in-process by the PyAV that ComfyUI ships.comfy node validatepasses;python tools/selftest.pyis the pre-push check.
Built for agents too
Since 0.3.5 the nodes are made to be driven from outside as well as by hand, through the ComfyUI API and Comfy MCP. Every input is a named slot with a tooltip an agent can read from object_info; new inputs arrive optional with a default, so an API export made before them keeps running. WSave Image declares every file it writes in /history (images, like the standard Save Image), and WDifference puts the run's diary in the PNG metadata so the file name can stay short and predictable. In WPrompt Rows each row is its own slot (text3, on3), so an agent can touch one line of your prompt and leave the rest alone.
Since 0.3.6 the saved widgets_values of every node match its object_info inputs one to one (buttons and pickers take no slot), so a positional reader of the workflow file, like comfy-cli's UI-to-API translator or MCP list_workflow_slots, pairs each value with the right input. A workflow saved by an older WextraUI is brought in line by python tools/migrate_outputs.py my.json (safe to run twice). Before a push, python tools/selftest.py --comfy <comfy.exe> my.json runs the same checks from outside the UI: registry lint, object_info, the file paired by position with every value type-checked, one small /prompt run per node. The rules every change follows are in DEVELOPING.md.
Changes
See CHANGELOG.md. Retired nodes live on in src/legacy/.
<p align="center"><sub>A Winteraction Lab product · by <a href="https://github.com/sickbraintwo">sickbrain2</a> · Apache-2.0</sub></p>