Dotted Waveform Visualizer
Turn Any Audio Into a Synced Dot Waveform Video — No Diffusion, No VRAM
- audio
- images
- audio
- fps_output
You just generated the perfect TTS voiceover - and now you're staring at a silent black timeline wondering what to show while it plays. That's the hole this node fills. Dotted Waveform Visualizer is a small, deterministic utility from the dotWaveform pack that turns any audio file into an animated, dot-based waveform you can render straight to GIF or MP4. No diffusion models, no sampling, no VRAM pressure. Feed it audio, get back a frame sequence that dances in time with it.
It's the lightweight end of the audio-reactive spectrum, and the launch thread on r/StableDiffusion pitched it that way on purpose. The heavyweight alternative - RyanOnTheInside's audio-reactive node suite - can split tracks, read MIDI, and drive whole workflows off amplitude, but it's a much steeper learning curve. This one does one thing, visibly, and gets out of the way. If all you want is a clean waveform visual to pair with a voice clip, that's the reach-for.
How it works
The node takes a ComfyUI AUDIO object (a waveform tensor plus its sample rate), splits it into frames at your chosen fps, and measures each frame's RMS amplitude. For spectrum and bars it runs an FFT with a Hanning window and normalizes a handful of frequency bands - the music-equalizer look, bass left, treble right. Then it draws dots onto plain PIL canvases and concatenates the frames into one batched IMAGE tensor. The whole thing runs on CPU; your GPU never wakes up.
Seven animation styles ship: scrolling, breathing, radial, bars, wave, spectrum, and circular. Each maps that same amplitude data onto the canvas differently - radial expands rings outward, circular grows a ring of dots, breathing pulses everything uniformly. A real FFT drives the frequency-based ones; the rest are pure amplitude.
The inputs that matter
animation_style- the one you'll actually change between projects. Everything else is polish.max_height- waveform height as a percentage of image height (5–150). Crank it for drama, drop it for a subtle bottom strip.fpsandmax_frames- 10 fps / 300 frames is about 30 seconds of output. Themax_framescap exists precisely so a long podcast doesn't hang your session rendering thousands of frames (0 = unlimited). Set it.sizeandspacing- dot diameter and gap. Smaller spacing = denser waveform.dot_color/background_color- hex strings (#00FFFFon black by default).window_size- scrolling only; how many seconds of timeline are visible at once.opacity_mode-uniform,3_levels,5_levels, or10_levels; more levels = smoother trails, at a small speed cost.preview_mode- swaps in a sine-wave stand-in so you can tune colors and style before processing real audio. Use it.
The optional gradient_enabled / gradient_stops and amplitude_boost (0.5–5.0x) come from the v2 UI - a visual gradient editor and color picker live in the JS frontend, and advanced_mode gates the boost (defaults to 1.0 when off). Headless users get these as widget values, gradient_stops as a JSON string.
The outputs
Three of them, and all get used: images (the IMAGE batch), audio (your audio passed through - actually improved), and fps_output (a FLOAT).
That passthrough is the clever part. The node force-converts the audio to float32 and, if it's mono, duplicates it to stereo - because VideoHelperSuite's VHS_VideoCombine expects stereo. So the canonical workflow is: TTS node → this → VHS_VideoCombine, with fps_output wired into VideoCombine's frame rate. The example workflow in the repo is exactly that (the AnimateDiff filename prefix is a leftover from a prior life; no AnimateDiff involved).
Installing it
ComfyUI Manager, search "dotWaveform", install, restart. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/Saganaki22/ComfyUI-dotWaveform
pip install -r requirements.txt
# restart ComfyUI
No model downloads - it's pure code. The catch is the dependency list: torchaudio, librosa, and scipy on top of what ComfyUI already ships. librosa pulls in numba and is a chunky install, and here's the quirk: the node imports it but never actually calls it. Still a hard dependency - if it's missing, the node won't load - but effectively dead weight on the clock. Budget a few extra minutes for the first install.
Where people get burned
- Long audio hangs - the default
max_framesof 300 is your friend. Raisefpsor forget the cap on a 20-minute file and you'll be waiting a while. - Console spam from VHS - the code has a whole fallback ladder for
LazyAudioMap(VideoHelperSuite's lazy audio wrapper). It usually succeeds, but it's chatty; a wall of "Method 3 (dict key)…" prints is normal, not an error. - Don't expect beat-mapped parameter control - the top comment on the release thread was someone asking to drive other nodes off the amplitude. That isn't built in; for that level of reactivity you want RyanOnTheInside's suite instead.
For a fast, deterministic "voice clip gets a visual" node, this is a tidy pick - deliberately simple, with a growing bug-fix log (2.2.0) rather than a feature parade. It renders, passes audio through cleanly, and it's one of the few nodes you can add to a workflow without worrying about your GPU or your registry.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | Audio file to create waveform visualization from | |
| width | INT | 1280100–1920 | Output image width in pixels. Default 1280 (landscape HD). Max 1920 for Full HD. |
| height | INT | 720100–1920 | Output image height in pixels. Default 720 (landscape HD). Max 1920 for square/portrait. |
| size | INT | 61–30 | Size of individual dots/bars in pixels. Larger size = bolder appearance. Range: 1-30px. |
| spacing | INT | 31–40 | Distance between dot/bar centers. Smaller values = denser waveform. Range: 1-40px. |
| dot_color | STRING | #00FFFF | Hex color for waveform dots. Examples: #00FFFF (cyan), #FF0000 (red), #00FF00 (green), #FFFFFF (white) |
| background_color | STRING | #000000 | Hex color for background. Examples: #000000 (black), #FFFFFF (white), #333333 (dark gray) |
| animation_style | COMBO | Choose animation style. SCROLLING: Continuous waveform. BREATHING: Pulsing dots. RADIAL: Expanding rings. BARS: Frequency bars. WAVE: Sine wave patterns. SPECTRUM: FFT frequency analyzer. CIRCULAR: Rotating circle. | |
| max_height | INT | 605–150 | Maximum waveform size as % of image height. Higher = taller waveforms. Affects all animation styles including radial radius. |
| fps | INT | 101–60 | Animation speed in frames per second. Higher FPS = smoother but larger file size. Range: 1-60 FPS. |
| max_frames | INT | 3000–1000 | Frame limit to prevent hangs on long audio. 0 = unlimited. 300 frames ≈ 30 seconds at 10 FPS. |
| opacity_mode | COMBO | uniform | UNIFORM: All dots same brightness (fastest). 3_LEVELS: 0%/50%/100% opacity. 5_LEVELS: More variation. 10_LEVELS: Smoothest gradients (densest look). |
| gradient_enabledopt | BOOLEAN | false | — |
| gradient_stopsopt | STRING | [] | — |
| amplitude_boostopt | FLOAT | 1.00.5–5 | — |
| advanced_modeopt | BOOLEAN | false | — |
| window_sizeopt | FLOAT | 2.00.1–10 | SCROLLING ONLY: Time window in seconds. How much audio timeline visible at once. Smaller = more detailed, larger = more overview. |
| preview_modeopt | BOOLEAN | false | Toggle ON for fast preview with sine wave pattern (no audio processing). Toggle OFF for normal audio-based animation. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | Generated waveform animation frames as image sequence |
| audio | AUDIO | Original audio data passed through unchanged |
| fps_output | FLOAT | Actual frames per second value used for the animation |