Nodes/comfyui-ethnicity_hairstyle_clip_encoder/CLIP Text Encode (Ethnicity/Hairstyle/Expression)
ComfyUI Node

CLIP Text Encode (Ethnicity/Hairstyle/Expression)

A text encoder that rolls a random ethnicity, hairstyle, and expression into every generation

By ajbergh·Created 2 years ago·Updated about a year ago· 2
CLIP Text Encode (Ethnicity/Hairstyle/Expression)
  • clip
  • positive_conditioning
  • negative_conditioning
text_positive
text_negative
ethnicity_toggleon
hairstyle_toggleon
expression_toggleon
ethnicityrandom
hairstylerandom
expressionrandom

This node is a drop-in replacement for the stock CLIP Text Encode that quietly rewrites your prompt before it ever reaches the model. Instead of just encoding whatever you typed, it prepends an ethnicity, a hairstyle, and an expression - either ones you pick from a dropdown, or a random roll on every single run. It's a variety machine: set all three to "random" and your batch of 30 images gets 30 different faces instead of 30 near-clones. Built by Adam Bergh (ajbergh), it's a small, single-file pack with no heavy dependencies - genuinely one of the simpler conditioning utilities you'll install.

Where it fits: this is SD 1.5/SDXL lineage tooling. The author's example workflow is SDXL with a LoRA, and everything about the design - comma-separated descriptors, prepending for emphasis, one CLIP input - assumes a CLIP-based checkpoint. If your whole stack is Flux or an LLM-encoded model, this isn't the node for you.

How it works

The mechanism is almost embarrassingly simple, which is why it works. On import, the node reads three CSVs sitting next to it in the pack folder (ethnicity.csv, hairstyle.csv, expression.csv - roughly 50–80 entries each), sorts them, and slots a "random" entry at the top of every list. When you queue a run, it builds your positive prompt by prepending whatever's selected:

  • ethnicity → "Asian person, "
  • hairstyle → "curly hair, "
  • expression → "happy expression, "

then hands the whole string to the standard clip.tokenize / clip.encode_from_tokens pipeline with pooling enabled. Each toggle can be flipped "off" independently, so you can keep the random expression while pinning a specific hairstyle. The negative prompt is encoded untouched.

There's one behavior you should know about before you rely on it. The node's IS_CHANGED returns the current time whenever any of the three dropdowns is set to "random", which forces ComfyUI to re-run the encode on every queue. That's how the random picks stay fresh - but it also means a "random" selection is not a one-time thing. Every generation rolls again, and every roll costs a re-encode. If you wanted "random once, then keep it," there's no lock button here; you'd have to read the chosen value off the workflow and pin it manually.

Inputs and outputs that matter

You mostly set four things:

  • text_positive / text_negative - your normal prompt and negative, both multiline.
  • The three toggles - ethnicity_toggle, hairstyle_toggle, expression_toggle ("on"/"off").
  • The three dropdowns - ethnicity, hairstyle, expression, each defaulting to "random".

The inputs you don't touch much: clip (wire in your CLIP model the same way you feed CLIPTextEncode). One honest tip: the node prepends descriptors, and earlier tokens carry more attention weight, so with all three toggles on your prompt's first words are the auto-generated ones. If output starts feeling generic, flip a toggle off and let your own subject lead.

Two outputs: positive_conditioning and negative_conditioning (both CONDITIONING). They wire straight into the sampler - it replaces a pair of standard CLIPTextEncode nodes with one.

Installing it

The easy way: ComfyUI Manager → Install Custom Nodes → search comfyui-ethnicity_hairstyle_clip_encoder, install, restart. Or by hand:

cd ComfyUI/custom_nodes
git clone https://github.com/ajbergh/comfyui-ethnicity_hairstyle_clip_encoder

then restart ComfyUI. There are no pip dependencies beyond what ComfyUI already ships (torch, etc.) and no model files to download - the CSVs travel inside the repo. It's MIT licensed.

Common issues

  • Console error about expressionWidgetWidget. Real bug, not you. The bundled JS extension that syncs your dropdown choices to ComfyUI settings defines expressionWidget but then references a typo'd expressionWidgetWidget when wiring the expression callback - so that callback throws when the node is created. The ethnicity and hairstyle callbacks are fine, and the Python side doesn't care about the JS at all, so generation still works. You'll notice it only if you rely on the expression choice persisting across sessions.
  • Dropdowns stuck after editing the CSVs. Options are read at import time. Edit ethnicity.csv and you'll need a restart (or a reload) before the menu reflects it.
  • Every run looks different when you didn't ask for it. That's the "random" default doing its job. Pin a dropdown or set a toggle to "off" for determinism.
  • "It ignores my prompt ordering." Not a bug - the descriptors are deliberately prepended. Restructure or toggle as above.
Categoryconditioning

Inputs (9)

NameTypeDefaultDescription
text_positiveSTRING
text_negativeSTRING
clipCLIP
ethnicity_toggleCOMBOon2 options: on, off
hairstyle_toggleCOMBOon2 options: on, off
expression_toggleCOMBOon2 options: on, off
ethnicityoptCOMBOrandom54 options: random, Aboriginal Australian, Afro-descendant, Ainu, Arab, Armenian, +48
hairstyleoptCOMBOrandom59 options: random, Afro, Asymmetrical Cut, Bald, Bantu Knots, Blunt Cut, +53
expressionoptCOMBOrandom86 options: random, Amused, Angry, Awestruck, Blinking rapidly, Bored, +80

Outputs (2)

NameTypeDescription
positive_conditioningCONDITIONING
negative_conditioningCONDITIONING