PNGTuber Quality Asset Builder (Image/Video)
Qwen-generated mouths, a THA3 idle loop, and a QA contact sheet that shows its work
- bundle_manifest
- mouth_track
- mouth_dir
- preview_contact
The upload-to-bundle node gets you a rig fast. This is the node you reach for when you care what it looks like. PNGTuberQualityAssetBuilder is the high-effort path: instead of cutting mouth shapes out of a video that may not articulate well, it generates them with a local Qwen image-edit model, erases the source mouth properly, optionally renders a THA3 idle loop so you have a natural mouthless animation, and - the part I like most - always writes a QA contact sheet that composites the generated mouths back onto the mouthless face so you can actually see whether it's a disaster before you ship it.
Image or video
Point source_path at either, and let source_mode sit on auto. Two behaviors:
- Image input (the common PNGTuber case - you have one good transparent character render): the node localizes the mouth, erases it, runs Qwen/eigen five times (closed/half/open/e/u) on the full source image with only the mouth area masked, and writes runtime sprites plus the overlay contact. If
make_tha3_loopis on, it renders a mouthless idle loop from the erased image. - Video input: Qwen generates identity-stable mouth sprites from the first frame while the video supplies the per-frame mouth track. That's a deliberate design choice - sprite identity stays consistent instead of cutting different mouths from different animation frames.
The quality rules are baked in: Qwen sees the whole face and character context, not a detached crop; mouth localization has to pass size sanity checks before a bundle is accepted; and the returned preview_contact is the overlay QA contact, not a raw sprite dump.
Inputs that matter
- source_path / asset_id / output_dir - where the input lives and where the bundle goes (empty
output_dir= ComfyUI output). - sprite_size / track_quad_scale / erase_scale / qwen_mask_scale / seed - the geometry and generation controls. Defaults are sane;
qwen_mask_scale(0.72) controls how big the edit mask is relative to the mouth. - make_tha3_loop - the big one.
trueby default, and it's the piece that needs external tools. tha3_repo points at a checkout of thetalking-head-anime-3-demorepo, tha3_skill_scripts at theai-live2d-thahelper scripts (env varsTHA3_REPO/THA3_SKILL_SCRIPTSare the fallback), and if either is missing it errors with a clear message. Settha3_variant(separable_floatis the default),fps,duration_seconds. - upscale_frames / upscale_target_px / upscale_model - the THA3 loop frames get upscaled (default
4x-UltraSharp.pthfrom yourupscale_modelsfolder) before encoding to webm + h264.
Outputs: bundle_manifest, mouth_track, mouth_dir, and preview_contact - all paths. The bundle is pngtuber.qualityAssetBundle.v1.
Install and honest expectations
Same pack install as the rest (Manager, or clone + pip install -r requirements.txt, restart). But unlike the plain video path, this node has real heavy dependencies:
- The local Qwen image-edit stack - the Qwen-Image-Edit model (GGUF or bf16), a Qwen text encoder + VAE, and the
TextEncodeQwenImageEditPlusnode. Multi-gigabyte downloads, same stack the "eigen" paths all share. - The THA3 repo and helper scripts for the idle loop.
- An upscaler model for
upscale_frames.
First runs also hit the "eigen" cache cold, so a 6-second THA3 loop plus five Qwen generations is not a five-second queue - budget for it. The upside is the QA gate: if the overlay contact shows the generated mouths sliding off the face or wrong-sized, don't ship - re-run with a bigger erase_scale or a manual bbox rather than tweaking blindly. It refuses obviously invalid mouth localization precisely so you don't ship a rig that looks broken on stream.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| source_path | STRING | input.png | — |
| output_dir | STRING | — | |
| asset_id | STRING | pngtuber_quality_asset | — |
| source_mode | COMBO | auto | 3 options: auto, image, video |
| sprite_size | INT | 512128–2048 | — |
| track_quad_scale | FLOAT | 1.251–4 | — |
| erase_scale | FLOAT | 1.181–2.5 | — |
| qwen_mask_scale | FLOAT | 0.720.3–2.5 | — |
| seed | INT | 420–2147483647 | — |
| make_tha3_loop | BOOLEAN | true | — |
| tha3_repo | STRING | — | |
| tha3_skill_scripts | STRING | — | |
| fps | FLOAT | 301–120 | — |
| duration_seconds | FLOAT | 6.01–30 | — |
| tha3_variant | COMBO | separable_float | 4 options: separable_float, separable_half, standard_float, standard_half |
| upscale_frames | BOOLEAN | true | — |
| upscale_target_px | INT | 1024512–2048 | — |
| upscale_model | STRING | 4x-UltraSharp.pth | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| bundle_manifest | STRING | — |
| mouth_track | STRING | — |
| mouth_dir | STRING | — |
| preview_contact | STRING | — |