msch-edge-ascii
Animate ASCII strokes on a photo, or restyle real video
- image
- video
- audio
- mask
- video
If you've used the sibling frames node from this pack, you know the look: geometric strokes that follow image contours plus halftone fills, like an engraved blueprint. This node is that same engine, but it puts the marks in motion over native video. Give it a still photo and it produces a looping clip of strokes sweeping and pulsing across it; give it actual footage and it restyles every frame while keeping the source's frame rate and audio. No model downloads - pure deterministic post-processing, which is exactly the kind of effect you want repeatable and cheap.
Why it's worth reaching for
The motion is the differentiator. Most ASCII/stylize attempts freeze one static treatment onto frames and call it video; this one animates the marks themselves - which matters because with static marks, a moving subject just looks like a still filter dragged over footage. Here the strokes breathe independently of the content, which reads as deliberate motion graphics rather than a frozen overlay. And it does it over the native ComfyUI video API: VIDEO in from Load Video, VIDEO out to Save Video, with source FPS and audio intact - no frame-batch encoding detour.
How the animation works
There are three animation choices. flow runs a traveling wave diagonally across the cell grid - in code, a phase built from each cell's row plus column position - so stroke intensity sweeps through the frame like light scanning across it. pulse is the simpler global version: the whole treatment brightens and dims together, more of a flash or blink. none (or an animation_strength of 0) applies the static treatment per frame. cycles sets how many complete oscillation cycles play across the clip, and with integer values a still photo loops seamlessly - the last frame hands off cleanly to the first. animation_strength (default 0.85) controls how deep the wave digs; drop it toward zero and the motion becomes a subtle shimmer.
Feed it a single photo and fps plus duration_seconds (defaults 24 and 4) decide the clip length. Feed it a VIDEO and the source's own FPS, frame count, and audio win - unless you also connect audio, which overrides the soundtrack. An IMAGE batch of existing frames is accepted too, keeping its own length.
Inputs to actually set
Beyond the four animation knobs above, every styling input from the frames node is here and behaves the same: mode (edge_only, full_ascii, mixed), cell_size (scale it with resolution - 16 is the default and suits ~512px sources), edge_threshold and edge_coherence for how many strokes survive, color as a hex string, background original or solid, and the optional mask where white permits the effect and black hides it. Wire exactly one source into image or video - connect both, or neither, and the node errors out on purpose.
What comes out
One output, a native VIDEO, which you feed straight into Save Video for an MP4. That's the whole point of choosing this node over its frames sibling: if your next step is a file, this keeps everything in video space. If you instead need to keep styling in IMAGE space for more frame-level work before encoding, use EdgeASCII and skip the video encode entirely.
Installing it
Same story as the frames node - one pack, nothing to download but code:
cd ComfyUI/custom_nodes
git clone https://github.com/mariobilly/msch-edge-ascii.git
cd msch-edge-ascii
python -m pip install -r requirements.txt
Then restart ComfyUI. Requirements are numpy, Pillow, torch, and av - all already present in a healthy ComfyUI (av is its own video backend), so the pip step is usually a no-op. Windows portable: run the pip command with .\python_embeded\python.exe. Note the pack is superseded by the unified MSCH Nodes repo (mariobilly/msch-comfyui-nodes) - follow its migration guide before switching, and above all don't run both, or you'll get duplicate node registrations.
Gotchas
Two real ones. First, this node needs a ComfyUI that ships the native video API - if your install is old enough that Load Video / Save Video don't exist, the VIDEO sockets won't behave and you should update ComfyUI first; no node is going to fix an engine that predates the video type. Second, don't hunt for it in ComfyUI Manager yet: the Registry listing was still pending verification when the pack shipped, so the git clone above is the dependable route for now. And as with the frames node, colors must be six hex digits - typing "red" earns you a runtime error, not a shrug.
Inputs (23)
| Name | Type | Default | Description |
|---|---|---|---|
| fps | FLOAT | 24.001–120 | Frame rate for a still photo or IMAGE batch. VIDEO input retains its own FPS. |
| duration_seconds | FLOAT | 4.00.1–60 | Duration when animating one photo. Existing videos and frame batches retain their length. |
| animation | COMBO | 3 options: flow, pulse, none | |
| cycles | INT | 11–32 | Complete animation cycles over the clip; integer cycles loop smoothly on a still photo. |
| animation_strength | FLOAT | 0.850–1 | — |
| mode | COMBO | 3 options: edge_only, full_ascii, mixed | |
| cell_size | INT | 164–128 | Grid spacing in pixels. Scale this with image resolution. |
| color | STRING | #FFFF00 | — |
| opacity | FLOAT | 1.000–1 | — |
| line_width | FLOAT | 1.60.5–16 | — |
| line_length | FLOAT | 0.850.1–1 | — |
| edge_threshold | FLOAT | 0.0550–1 | Lower values create more strokes; higher values keep stronger contours. |
| edge_coherence | FLOAT | 0.250–1 | Raise to remove strokes from noisy areas with conflicting directions. |
| blur | FLOAT | 1.20–12 | Smooth the detection image only; the original photo stays sharp. |
| tone_gamma | FLOAT | 1.60.1–4 | — |
| fill_amount | FLOAT | 0.500–1 | Full ASCII: tone-pattern opacity (use 1 for dense yellow). Mixed: shadow coverage. Edge only: unused. |
| invert_tones | BOOLEAN | false | — |
| background | COMBO | 2 options: original, solid | |
| background_color | STRING | #000000 | — |
| imageopt | IMAGE | Connect a single photo to animate, or a batch of video frames. Connect either image or video. | |
| videoopt | VIDEO | Connect Load Video; its FPS, frame count and audio are preserved. | |
| audioopt | AUDIO | Optional soundtrack; overrides audio from VIDEO input. | |
| maskopt | MASK | White permits the effect, black excludes it. Single mask broadcasts over the batch. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | — |