Tags Generator
Let 5.48 Million Danbooru Posts Finish Your Prompt
- text
The single most repeated piece of advice for anime models is also the least actionable: use real
Danbooru tags, because the model understands them so much better than your sentences. Great. Now
which tags? You know 1girl. You may not know that the wall of furniture you keep getting is
because you typed bar and Danbooru spells it bar_(place).
Tags Generator answers that by counting. Feed it a prompt and it appends 15 tags that statistically travel with what you wrote. No LLM, no API key, no model load - arithmetic over a co-occurrence table built from 5.48M Danbooru posts.
What it actually does
The name says generator; the mechanism is a sampler. Tags are drawn one at a time, the way a
language model emits tokens: score(t) = log P(t) + Σ log lift(t, context). That's naive Bayes over
the corpus. A tag is only a candidate if something already in the prompt pulls it - positive
attraction, not merely high frequency - and it's ranked by how much rarer than chance that
attraction is. Rare tags get penalised by log P(t), the vocabulary is capped at 20,811 tags, and
anything outside it is ignored silently. Each pick then joins the context and re-conditions the next
one, which is why the output reads like one scene instead of a shopping list.
The same statistic that powers the pack's conflict filter runs in reverse here: lift well below 1 means two tags avoid each other, so those candidates get masked and the output can't contradict itself. Rating works on both halves of the table - the numbers come from the matching corpus slice, and tags rated above your request are masked outright.
It's genuinely the difference between this and the click-and-play prompt pages people share: theirs is a list someone liked, this is measured against your exact prompt.
The inputs you'll actually touch
text is a socket (forceInput), so you wire a String primitive or another node's output into
it. Its tags condition every pick and are never filtered - they just have to be spelled the way the
corpus spells them.
n (default 15, 0–100) is how many tags get added, counted after post-processing. Set it to 0 and the length is drawn from the corpus, with generation stopping early once nothing is clearly better than chance. Good default for exploring.
The six category toggles and shares are the real steering wheel, and they work relatively, not
as fractions: switch a category off and its share is handed to the ones still on, so you rarely get
fewer tags. Defaults are pose 0.3, expressions 0.2, clothes 0.2, characters 0.1, body 0.1,
background 0.1; -1 means allowed with no cap. Two gotchas: characters owns the subject itself
(1girl, solo), so turning it off removes the gender anchor everything downstream conditions on;
and background is really the whole scene - objects and compositions draw on its single budget,
which exists precisely to stop an unrestricted draw filling your prompt with furniture.
momentum (0.5) decides how much each new tag conditions the next: low and the tags have nothing to do with each other, high and it reads as one scene that can wander off into its own subject.
rating (all by default) is a ceiling, not a target - a nude prompt draws explicit tags, a
school uniform one draws none. random rolls the cap from the seed, so a new seed rerolls it.
The rest of the widgets are for when the output is wrong in one specific way - hover them, the author wrote real tooltips.
Outputs, install, and the 106MB
One output, a STRING. The README calls it processed_text; the shipped node names it text -
wire that into your positive CLIP Text Encode. Your original tags come first, the drawn ones
appended after.
Install via ComfyUI Manager (search ComfyUI-Generator-Pack), or:
cd ComfyUI/custom_nodes
git clone https://github.com/alchemine/comfyui-generator-pack
No pip dependencies, Python ≥3.12. The data is the real install: nothing ships in the repo, and the
first run pulls suggest_v1.1.npz at ~106MB from the pack's GitHub releases, plus a 14MB avoidance
table and a few small files, each pinned by sha256. The pack's other two nodes - Group Tags (themed
lines) and Classify Tags (splits a prompt into buckets) - share the label data and never touch that
106MB.
Where people get burned
First run looks hung, then the node does nothing. That's the download - over 100MB, with the only
sign a console line, so give it outbound access to github.com once. If the suggestion table is missing
or fails to verify, you'll see suggest artifact not found; passing through and your prompt comes
back unchanged: the node degrades instead of erroring, so identical output means read the console, not
the graph.
Shorter than n. You'll see only X of Y tags after N rounds -- lower lift_threshold or min_count, or relax blacklist and categories. Causes, in order: a share that ran out of material, a
category you switched off, filter_subtags eating a pick because another tag already implies it
(keeping white dog over dog), or an aggressive blacklist. Loosen the smallest thing first.
Every pick is the same idea respelled - blue skin, pale skin, dark skin. That's
repetition_penalty, or momentum pulling along the axis it just moved on. Raise the penalty, or
lower momentum.
Tags your model has never learned. Raise min_count above the 100 default; counts are counted
within the requested rating tier, so a milder rating already shrinks the pool.
Same tags every run. That's reproducibility, and the seed reverts it: put the seed on randomize
in control_after_generate, and set ComfyUI's "widget control mode" to Before so the number in the
box is the one that actually ran. If changing the seed does nothing at all, check temperature - at
0 the node always takes the best candidate and the seed is irrelevant.
Inputs (30)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | The prompt to extend. Its tags condition every pick and are never filtered themselves. Tags outside the 20,811-tag vocabulary are ignored silently -- Danbooru spells a bar 'bar_(place)', not 'bar'. | |
| n | INT | 150–100 | How many tags to add, counted after post-processing. 0 = auto: the length is drawn from the corpus and generation also stops early once nothing is clearly better than chance. |
| characters | BOOLEAN | true | Allow characters tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| characters_share | FLOAT | 0.10-1–1 | How much of the output characters may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| pose | BOOLEAN | true | Allow pose tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| pose_share | FLOAT | 0.30-1–1 | How much of the output pose may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| expressions | BOOLEAN | true | Allow expressions tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| expressions_share | FLOAT | 0.20-1–1 | How much of the output expressions may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| body | BOOLEAN | true | Allow body tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| body_share | FLOAT | 0.10-1–1 | How much of the output body may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| clothes | BOOLEAN | true | Allow clothes tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| clothes_share | FLOAT | 0.20-1–1 | How much of the output clothes may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| background | BOOLEAN | true | Allow background tags at all. Switching it off hands its share to the categories still on rather than shrinking the output. |
| background_share | FLOAT | 0.10-1–1 | How much of the output background may take, relative to the other categories that are on: with only pose 0.2 and expressions 0.1, ten tags come back 7 and 3. -1 = allowed with no share of its own. |
| lift_threshold | FLOAT | 0.100–0.5 | Veto strength. A candidate is banned when the corpus expected it alongside a prompt tag often enough (>= 15 posts) and it still came in below this fraction of chance. Raise it when the output contradicts the prompt in ways the data merely discourages; 0.1 only catches pairs that essentially never co-occur. |
| momentum | FLOAT | 0.500–1 | How much each generated tag conditions the ones after it. 0 = every tag answers to the prompt alone and they have nothing to do with each other. 1 = a pick counts as much as a prompt tag, so the output reads as one scene but can wander off into its own subject. |
| repetition_penalty | FLOAT | 2.01–10 | Divide a tag's odds by this for every tag already in the prompt that varies along the same axis -- the same last word ('<colour> skin'), or the same half of a linking word ('hands on own face' / 'hands on own head'). 2.0 halves them each time, so a second needs twice the evidence the first did and a third needs four times; 1.0 turns it off. Counters momentum, which pulls hardest along the axis it just moved on. Exact repeats are blocked outright and are not what this controls. |
| rating | COMBO | all | Explicitness ceiling, on both halves of the statistic: the co-occurrence tables come from the matching corpus slice, and tags rated above the request are masked. It is a ceiling, not a target, so a named rating also gets a nudge toward itself -- 'explicit' would otherwise merely permit rather than lean. 'all' caps and favours nothing, leaving the prompt to decide: a nude prompt draws explicit tags, a school uniform one draws none. 'random' picks one of the four from the seed instead, each equally likely -- a capped draw every time, but a different cap on the next seed. |
| temperature | FLOAT | 1.000–5 | Sampling randomness. 0 = always take the best candidate, which makes the seed irrelevant and every run identical. Higher spreads the picks over weaker candidates. |
| top_k | INT | 00–500 | Sample from this many best candidates per step. 0 = no limit. Ignored at temperature 0. |
| top_p | FLOAT | 0.950–1 | Keep the best candidates adding up to this much probability. 1.0 = no limit. Watch out for 0, which leaves exactly one candidate and turns sampling back into greedy picking. |
| min_p | FLOAT | 0.050–1 | Drop candidates below this fraction of the best candidate's probability. 0 = off. |
| min_count | INT | 100100–1000000 | Ignore tags with fewer than this many posts in the requested rating tier. The default is the vocabulary floor, i.e. no filtering. Raise it when a prompt keeps surfacing tags too obscure for your model to have learned. |
| replace_underscoresopt | BOOLEAN | true | Write tags as 'blue eyes' rather than 'blue_eyes'. |
| filter_tagsopt | BOOLEAN | true | Drop duplicates and blacklisted tags from the finished prompt. |
| filter_subtagsopt | BOOLEAN | true | Drop tags another tag already implies, keeping 'white dog' over 'dog'. It can eat a pick the sampler just made, which is why the node asks for replacements until n survive. |
| filter_copyrightopt | BOOLEAN | true | Drop candidate tags owned by one character or one franchise -- a tag whose posts mostly carry the same character (crescent hat ornament) or the same copyright (mini hakkero). Keeps a library from turning into Patchouli's library. Tags you typed yourself are never dropped. |
| order_tagsopt | BOOLEAN | true | Return the added tags grouped by kind -- subject, body, expressions, pose, clothes, scene -- so the same settings put the same kinds of tag in the same place. Off keeps the order they were drawn. The input prompt is never reordered. |
| blacklistopt | STRING | Regex matched against each candidate tag in spaced form, case-insensitively: 'hair|eyes' drops every hair and eye tag. It filters candidates rather than results, so n tags still come back. Use '|', not commas. | |
| seedopt | INT | 00–18446744073709550000 | Reproducibility. The same seed and settings always give the same tags -- unless temperature is 0, where the seed does nothing at all. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | — |