Note
New: v2a-pre1 and v2a-pre2 preview builds with a much stronger character mode, several references of one character, and per-reference weights. What is new in v2a. v1a stays the recommended release.
Important
Please support SnapRef! Developing these models has taken 400+ H100 GPU-hours so far. If SnapRef is useful to you, a β on GitHub, sharing your results or reporting issues helps a lot, and GPU sponsorship is very welcome.
Give Anima one picture of a character. Write only the scene.
Hair, eyes and outfit come from the reference and follow the new pose. The art style comes from the reference too,
or, with mode char, from the base model and your prompt.
Quick start Β· v2a preview Β· Results Β· How it works Β· Training Β· Roadmap
This repository holds the training, data generation and evaluation code behind SnapRef, a reference adapter
for Anima base v1.0. The released weights, ComfyUI node and
workflows are on Hugging Face: snapref-v1a (recommended),
snapref-v1b (twice the LoRA rank), and the preview builds
snapref-v2a-pre1 and
snapref-v2a-pre2. The node in comfyui_nodes/snapref/
works with all of them.
| β‘ Fast | The reference is encoded once and its keys/values are cached. Generation is about 15 % slower than plain Anima, and faster than the other Anima reference adapters. |
| π― Scene-only prompts | No character name, appearance or artist tags. The reference carries them. |
| π Drop-in node | One ComfyUI node patches the model. Works with the bf16 and the FP8 Anima base. |
| π¨ Character reference | mode char takes the character and leaves the reference's art style behind (much stronger in v2a). |
| π₯ Two characters | Stitch two references side by side and prompt 2girls. |
| π§© Several references, one character | image_2, image_3: e.g. an upper-body shot for the face and a full-body shot for the outfit. |
| βοΈ Reference weights | weight_1 β¦ weight_3: how much each reference counts, without slowing generation down. |
-
Install Anima base v1.0 with its text encoder and VAE in ComfyUI.
-
Copy
comfyui_nodes/snapref/from this repository toComfyUI/custom_nodes/snapref/(replace an older SnapRef node). This is the newest node; it works with every SnapRef version. -
Put the weights in
ComfyUI/models/loras/:weights snapref_v1a.safetensorsrecommended snapref_v2a_pre2.safetensorspreview: stronger mode char; try this one first of the two previewssnapref_v2a_pre1.safetensorspreview: earlier checkpoint snapref_v1b.safetensorstwice the LoRA rank of v1a Each also comes as an FP8 file (half the size).
-
Open the
*_simple.jsonworkflow from the same Hugging Face page (workflows/), click the picture slot in the SnapRef node and queue.
masterpiece, best quality, score_7, safe, 1girl, solo, full body, sitting, crossed legs, bench, street, night
Note
SnapRef is not a regular LoRA. Load it with a SnapRef node, not with LoraLoader.
Two nodes.
| node | for | inputs |
|---|---|---|
| SnapRef | everyone | one or two pictures picked inside the node (click or drop), Mode (Character & Style / Character), Strength per picture, Fidelity |
| SnapRef (Advanced) | full control | every setting (below and in the v1a model card); saved workflows open on it unchanged |
On the simple node, Strength is how much a picture counts (1 = as trained, 0 = off, 2 = twice) and Fidelity trades detail for prompt freedom (1 = every detail of the picture; lower also lets the negative prompt branch read the picture, so the prompt is followed more freely, about 12 % slower).
Fidelity is also available on the advanced node (0β1, default 1). It is ignored when
ref_in_uncond or ref_guidance_on is on. It is appended after the existing inputs to preserve saved workflows.
Inputs of SnapRef (Advanced) added for v2a:
| new node input | default | |
|---|---|---|
image_2, image_3 |
more references of the same character (e.g. full body when image is an upper-body shot) |
|
use_batch |
off | use every image of an IMAGE batch as a reference |
multi_layout |
separate |
separate: each reference on its own. stitch: side by side in one reference (same speed as one image) |
weight_1 β¦ weight_3 |
1.0 | how much each reference counts (0β2, 0 = skipped); separate layout only |
The new inputs sit at the end of the node, so saved v1 workflows open unchanged.
Two checkpoints of the v2a run, released as previews: v2a-pre1 (step 10,000) and v2a-pre2 (step 15,000). Both continue from v1a on a mix of real Danbooru illustrations (80 %) and Anima-generated images (20 %). Try pre2 first; pre1 is kept for comparison. Neither replaces v1a.
Character reference (mode char). The largest change. v2a takes the character from the reference and leaves
its art style behind; the base model and your prompt set the look.
Several references of one character. Each reference is encoded on its own and the image reads all of them.
Reference weights. log(w) on that reference's attention keys, computed without an attention mask (fused
attention per key group, merged by log-sum-exp), so strength and weights no longer slow generation down.
How v2a compares to v1a (detail evaluation, 64 cases Γ 2 seeds, difference to v1a, 95 % CI):
| v2a-pre1 | v2a-pre2 | |
|---|---|---|
| appearance tags kept β | β0.014 [β0.047, +0.019] | β0.015 [β0.051, +0.021] |
| head-crop SigLIP2 β | β0.018 [β0.025, β0.011] | β0.005 [β0.010, β0.001] |
| head-crop CCIP distance β | +0.010 [+0.003, +0.017] | +0.005 [+0.001, +0.009] |
| head-crop CCIP distance, real-art references β | +0.019 [+0.009, +0.032] | +0.010 [+0.004, +0.015] |
In cs mode neither build is better than v1a yet; pre2 is close. Real illustrations as training targets did not
help, so the next run (v2a-syn) uses Anima-generated images only: 121,642 images of 5,886 characters
(configs/datagen_scenes_v2.json, datagen/clean_tags_v2.py). The evaluation is in train/detail_eval/.
Characters that were not in the training data. Every method gets the same prompt and seed; the prompt describes only the scene.
Measured on 32 held-out characters (52 cases in total, same seeds), see
train/score_panels.py:
| SnapRef v1a | ElectronGirl v2 | Anima, no reference | |
|---|---|---|---|
| DINOv2 similarity to the true image of the character β | 0.722 | 0.675 | 0.493 |
| CLIP similarity to the true image β | 0.863 | 0.844 | 0.670 |
| seconds per image, A100, 832Γ1216, 28 steps β | 8.9 | 14.4 | 7.7 |
| held-out loss identity gap β | 0.0040 | β | β |
The "true image" is another picture of the same held-out character in the prompted pose. Role-Ref-KV was timed at 12.5 s on the same setup.
flowchart LR
R[reference image] -->|VAE, Ο = 0| RB["reference branch<br/>(ref LoRA)"]
RB -->|"K/V of every layer<br/>computed once"| C[(K/V cache)]
N[noisy latent + scene prompt] --> TB["target branch<br/>(tgt LoRA)"]
C -.->|"read at every step"| TB
TB --> O[image]
- Asymmetric attention. The target tokens attend to themselves and to the cached reference. The reference never attends to the target, so it can be computed once, before sampling starts.
- Two LoRAs. The reference branch and the target branch each have their own LoRA (v1a: rank 128 and 64).
- Separate position. The reference sits at its own place in the model's RoPE coordinates, so it is read for appearance and not copied as a layout.
- Several references. Each reference is encoded on its own and the cached K/V of all of them are
concatenated; a reference weight adds
log(w)to that reference's keys. - Training data. v1a: 58,300 images generated with Anima itself (
scenes3x4): 2,367 characters, each drawn in several scenes and poses in one artist style. The training prompt leaves out what the reference supplies. v2a: continued from v1a with 231,724 Danbooru illustrations (33,666 characters) as 80 % of the pairs.
# 1. data: scenes x poses per character, rendered with ComfyUI (one worker per GPU)
PROJECT_ROOT=$PWD bash deploy/run_scenes3x4.sh production
# 2. text contexts + latents -> safetensors, then train, preview and compare every checkpoint
GPUS=0,1 FIRST=v1a bash deploy/train_pipeline.sh # configs/train_asym_<name>.json
# 3. score a comparison run
python train/score_panels.py RUN_A RUN_B --label a b --out scores.json
# v2a: continue from v1a on the merged real + generated prep (configs/train_v2a.json)
python train/train_asym.py --prep v2_mix_prep --config configs/train_v2a.json --init snapref_v1a.safetensors --out runs/v2a| path | |
|---|---|
train/asym.py |
asymmetric attention, reference K/V cache, the two LoRAs |
train/prepare_asym.py, train/train_asym.py |
tensor preparation and training |
train/eval_asym.py, train/compare_refs.py, train/score_panels.py |
held-out loss gaps, side-by-side runs against other adapters, embedding scores |
train/anima_env.py, train/sample.py, train/preview.py, train/prepare*.py |
model plumbing on top of ComfyUI, sampling, in-training previews, shared data helpers |
comfyui_nodes/snapref/ |
the ComfyUI nodes: SnapRef (simple, simple.py + web/snapref.js) and SnapRef (Advanced) |
train/detail_eval/ |
detail evaluation: frozen set, renders, tag / head-crop features, paired CIs, watcher |
train/multiref_test.py, train/ccip_eval.py |
multi-reference test, CCIP identity scores |
datagen/select_v2_images.py, datagen/build_pairs_v2.py, train/D_build_v2_prep.py, train/D_merge_preps.py |
v2a data: Danbooru selection (tags only), training pairs, prep, merge with the generated prep |
datagen/clean_tags_v2.py, configs/datagen_scenes_v2.json |
the scenes_v2 synthetic data run (v2a-syn) |
train/style_research/, train/style_*.py |
the v2 style-transfer experiments (did not beat the baseline) |
datagen/ |
tag catalogs, scene manifests, pose extension, ComfyUI batch workflow and GPU worker |
configs/ |
the configs of the released runs: datagen_scenes.json, train_asym.json (v1a), train_asym_v1b.json, train_v2a.json (v2a-pre1 / pre2), datagen_scenes_v2.json |
comfyui_nodes/anima_ref_datagen_nodes/ |
batched generation nodes used to render the training data |
deploy/ |
GPU box setup, data generation launcher, training pipeline, FP8 conversion |
Data generation and training of v1a alone took about 120 H100 GPU-hours.
- v1a: asymmetric attention + K/V cache, 58k generated images. Released.
- v1b: twice the LoRA rank. Keeps the character as well as v1a and follows the prompt slightly more closely. Released as snapref-v1b.
- v2a-pre1 / pre2: continued from v1a with 80 % real Danbooru illustrations. Much stronger
mode char; multi-reference and reference weights in the node. Preview builds (Hugging Face, opening soon). - v2a-syn: generated images only, 121,642 images of 5,886 characters (2.5Γ the characters of v1a). Goal: beat v1a on the detail evaluation, especially faces with real artwork as the reference.
- Multi-reference training (2β3 references β another pose), style transfer (v2.1+).
- Black image with an fp16 model or
--fast. Fixed in the node update of 2026-10-31. Anima overflows in fp16. ComfyUI runs the model in fp16 when the checkpoint is stored in fp16 (many merges), with--fp16-unet, or with--fast/--fast fp16_accumulation. The old node encoded the reference in fp16 there and produced NaN. The new node keeps that part in fp32, as ComfyUI itself does. bf16 output is unchanged (same bits). With the old node, add--bf16-unetto the ComfyUI launch arguments, or dropfp16_accumulationfrom--fast. - Anzhc Mod Guidance: works in either node order with the updated node. Before, applying Mod Guidance first dropped the reference.
- Spectrum: works with
ref_guidance_onoff. With it on, Spectrum-type step forecasting breaks; the node now logs a warning. - sorryhyun Mod Guidance: with the default settings it can stop with a tensor-size error (4 vs 2). Turning on
ref_in_uncondorref_guidance_onavoids it. - SageAttention: with
strengthor reference weights β 1, the reference part uses PyTorch's fused attention, so a GPU where Sage is much faster can be slightly slower there. - Reference cache: the reference is encoded once and kept between runs while the picture and settings stay the same (about 0.9 GB of VRAM for a 1 MP picture); "Free model and node cache" in ComfyUI releases it.
- Very small details (earrings, fine patterns, scars) can be simplified.
- A dark-skinned reference can come out with lighter skin.
- A scene can override the reference outfit (beach β swimsuit). Raise
strengthor name the outfit. - v2a previews: faces match slightly less closely than with v1a, most of all with real artwork as the reference.
- v2a
mode char: a few outfit details of the reference can change (a patterned item drawn plain, a hat left out).





