character-refs
Input a paragraph of character description, and output a set of character reference images — characters can come from anywhere: novel adaptations, original stories you made up, a person you thought of on the spot. One front-facing full-body anchor image, all other images only reference this anchor image.
Can be used as reference images for video models (H3 / Wan / Seedance), and also for human viewing.
= the directory where this file is located. The script
{baseDir}/scripts/character-refs.mjs
has zero dependencies and runs directly with
.
This is the only skill in the repository that actually generates images. All other skills only provide prompts, because the model and art style can only be decided at the moment of image generation;
This skill is that "moment" — the model is selected by the user in Step 0, and the art style is selected when creating the character (default is anime, there are also realistic presets, and custom art styles can be attached).
Core Rules (Read carefully before starting)
-
A set of images has only one root: the front-facing full-body anchor image. Only it is text-to-image generated; all others are "anchor image + one image editing instruction".
Three types of details — hair, face, and neckline — additionally use a confirmed front-facing close-up portrait. Images do not reference each other —
so if any one image turns out bad, just regenerate that single one, no chain reaction.
-
Tiered generation, default up to Tier 2:
| Tier | Content | When to generate |
|---|
| 1 | Front-facing full-body (anchor) | Always, first image |
| 2 (default) | Front-facing close-up portrait, 90° side view (facing right side of the frame), back view | Generated by default |
| 3 | Detail shots (max 8): features specified by the user, the most recognizable parts of the character; if none, use default hair/hair accessories, neckline, cuffs, shoes | When there are accessories or close-ups of clothing parts |
| 4 (not generated by default) | 45° close-up portrait (facing left side of the frame) | When explicitly requested by the user |
H3 actual test: with only front-facing full-body image, clothing and back view are correct, but the face will be distorted;
adding a close-up portrait makes the face most accurate. That's why Tier 2 is generated by default.
If you only want the anchor image (e.g. check the appearance first):
gen <asset.json> --tier 1
.
-
No model lock-in: The main image and derived images can use different models, and you can also switch models when regenerating a single image. Consistency comes from "the same anchor image",
not from "the same model" (actual tests show Qwen anchor + GPT derived, GPT anchor + Qwen derived, and mixed use in the same set all work well).
Each image records its own model, and mixed model use in the same set is only reminded in the report.
-
Only three things are frozen: anchor image file (sha256), art style snapshot, text description fingerprint. If any of them changes,
images that depend on it are marked as "expired" — only marked, not deleted, all old versions are kept.
Step 0 — Initial Setup (once per machine)
bash
node {baseDir}/scripts/character-refs.mjs config --show
If the output ends with "Missing: ...", you need to ask the user these three things, ask all at once:
- Which model to use for the main image (anchor):
- — Qwen Image 2.1, runs on your own ComfyUI server. Fast (25–45 seconds per image), precise dimensions, does not consume subscription quota.
Requires (optional / ), placed in environment variables or a .env file
- — codex built-in image generation (GPT). No API key required, but consumes ChatGPT subscription quota (Plus plan hits limit at ~25 images, recovers after a few hours), 80–160 seconds per image
- — OpenAI Images API, default . Pay-as-you-go, requires ; can also point to compatible endpoints ()
- — user's own image generation command, see
- Which model to use for derived images: Defaults to the same as the main image. Can be set separately (e.g. GPT for anchor, Qwen for derived images)
- Whether to stop and wait for human confirmation after generating the anchor: Recommended — the anchor is the root of the entire set, if it's wrong all subsequent images are wasted.
Selecting will auto-approve, marked as "not manually confirmed" in the report; anchors that fail the check gate will not be auto-approved, they require human review then or regeneration.
bash
node {baseDir}/scripts/character-refs.mjs config --model qwen --confirm-anchor yes --qwen-env-file <.env path>
# Separate settings: --anchor-model codex --derive-model qwen
Configuration is written to
{baseDir}/config.local.json
(not under version control, will not be overwritten by reinstallation). It will be used by default afterwards,
if the user says "switch to xx", re-run
.
Do not print passwords or keys from .env,
already masks them.
Step 1 — One-time Input
The user only needs to describe the character in one paragraph, don't ask item by item. You (the model) split it:
bash
node {baseDir}/scripts/character-refs.mjs intake-template
Split the description into various fields according to the template, and write to
<working directory>/<character-name>-intake.json
. Rules (see
for details):
- fills in the language the user speaks (Chinese , English , Japanese are built-in). Both the confirmation form and report use this language;
For other languages, first run , translate the printed English text item by item, and put the whole block into the field of the intake file
- Content stated by the user is marked , content you infer is marked , content supplemented by convention is marked — mark truthfully, the confirmation form relies on this to remind the user which items to check
- If you can't infer age, gender, or era, ask the user — only these three things are not allowed to be made up; all others are supplemented by yourself, don't ask follow-up questions
- field goes into prompts: always English, no character name, no art style words; is for human reading, written in the language (old inputs written in are still recognized)
- Clothing is split into / — close-up portraits only use the top, if both top and bottom are included the model will pull the camera back
- Select detail shots based on the character: user-specified features take priority, if none, select the most recognizable parts of the character, with four default slots as fallback; facial features do not get detail shots (they are already clear in close-up portraits). See for details.
bash
node {baseDir}/scripts/character-refs.mjs intake-check <intake.json>
If there are issues, fix them according to the error messages until it passes. After passing, it prints a confirmation form, show it to the user as-is, with inferred and default items marked.
Only proceed after the user confirms (or confirms after modifications).
Step 2 — Create Asset
bash
node {baseDir}/scripts/character-refs.mjs new <intake.json> --out <output directory> [--look art style]
Creates
<output directory>/<character-name>/asset.json
. The entire art style layer is snapshotted into the asset at this point, subsequent preset changes will not affect existing characters.
Art style: Select according to user's request if specified, default is anime. Characters from the same work
all use the same art style.
| Preset | Names recognized by |
|---|
| Anime (default) | |
| Realistic photo | |
lists all presets. If the user wants another art style (Chinese style, American comics...): run
look-template <closest preset> > my-look.json
, modify
(medium, lines),
(background and lighting,
white background must be retained),
(negative prompts — when switching to a drawn art style, remove
from negative prompts),
for drawn art styles set
to
, then use
. Custom art styles have not been actually tested, generate the anchor image first to check the effect.
To change the art style of an already created character:
bash
node {baseDir}/scripts/character-refs.mjs restyle <asset.json> --look anime
After changing, all existing images (including the anchor) are marked as expired, old images are kept, and regeneration starts from the anchor.
Step 3 — Generate Default Tier 2
bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json>
Without specifying views or tiers, this command fills in all ungenerated and expired images in Tier 1 and Tier 2.
If confirmation is configured: this command stops after generating only the anchor image. Show the image to the user (via report or directly opening the PNG), and after the user approves, run:
bash
node {baseDir}/scripts/character-refs.mjs confirm <asset.json>
node {baseDir}/scripts/character-refs.mjs gen <asset.json> # Same command, continues to generate close-up portrait, side view, back view
If not satisfied, run
gen <asset.json> front-full
again (generates v2, can use
to change seed,
to change model),
or go back to Step 1 to modify the description.
If the anchor is not confirmed, derived images will be rejected. When confirmation is disabled, one command generates all four images.
Step 4 — Upgrade Tier as Needed
bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json> --tier 3 --reason "E03 has hair accessory and neckline close-ups"
records why the tier is upgraded, and is stored in the asset. You can also generate only specific images:
gen <asset.json> detail-hair detail-neck
.
Step 5 — Check and Regenerate
bash
node {baseDir}/scripts/character-refs.mjs check <asset.json>
Each image passes through a code gate (dimensions 300–5760, aspect ratio 0.4–2.5, correct proportions, ≤20MB, background is white enough), then checks for expiration.
Exits with code 1 if there are issues. For images that fail the gate or are expired, regenerate that single image:
bash
node {baseDir}/scripts/character-refs.mjs gen <asset.json> detail-neck --model codex
For the "background is white enough" check in the gate: for full-body images, check all four edges; for close-up portraits, only check the top edge and upper half of both sides (the body naturally reaches the bottom); for detail shots, this check is skipped and explicitly stated.
The gate cannot check appearance, angle, or framing — you must look at the images after generation, these items rely on human eyes.
Step 6 — Report
bash
node {baseDir}/scripts/character-refs.mjs render <asset.json>... --out <output directory>/character-refs.html
Multiple characters can be generated together. The language defaults to the asset's
, add
to temporarily change it; for non-built-in languages without
, use
to pass self-translated text,
missing text will directly throw an error, no half-Chinese half-English reports will be generated. Interface text changes with language, character descriptions remain in original language.
Tier 1 has only one card; from Tier 2 onwards it's a concept art layout (close-up portrait on the left, front/side/back views on the top right, detail bar on the bottom right),
detail shots are not shown by default, can be toggled with one click on the page. Expired images are marked with red borders, there is a legend at the top of the page, and the reason can be seen when hovering over red-bordered images; mixed model use is also reminded.
Step 7 — Report Back
In one sentence: state which images were generated, which model was used, and the report path. Name any images that failed the gate or are expired. Do not say "the effect is great" if you haven't looked at the images.
Output Structure
<output directory>/
├── character-refs.html ← render output
└── <character name>/
├── asset.json ← character description + all versions of each image
└── default/ ← outfit (v1 only has default set)
├── front-full.v1.png ← anchor
├── face-front.v1.png
├── face-front.v2.png ← regeneration does not overwrite
└── detail-hair.v1.png …
Each PNG has embedded identifiers (
character/outfit/view/version
), model, and which images it references — even if the image is copied separately, its origin can be traced.
See
for the structure of
.
Limitations
- Only two art styles have been actually tested: realistic and anime (both run all four tiers on Qwen); custom art style effects depend on the prompt writing, generate the anchor image first to check
- Detail shots only include features on the character's body (hair accessories, neckline, cuffs, shoes); props belong to novel-art
- Known shortcomings: small scars are easily drawn as fresh wounds, large-area details like double-breasted buttons have distant framing, Qwen's neckline details make coarse cloth look like velvet (regenerate that single image with codex), exposed skin in Qwen detail shots looks older (age sentences have been added to prompts, effective on 16-year-old samples, not extensively verified)
- The adapter has only been tested for request format using a local fake server, has not been run with a real key; generate an anchor image first when using it for the first time
- The same set of prompts has been actually tested on both Qwen and GPT; custom model effects depend on that model.
Self-test
bash
node {baseDir}/scripts/selftest.mjs
230 assertions, no model calls, no quota consumption: PNG identifier read/write, check gate, input validation, multilingual confirmation forms and reports, art style presets and style switching, prompts, expiration propagation, request format of image generation adapters (Qwen and GPT Image use local fake servers),
and the complete workflow run with custom commands. Run this after modifying the script.
Built-in Examples
{baseDir}/examples/阿禾-描述.txt
is a user-style one-paragraph description (1920s Jiangnan tea-picking girl),
is the parsed input, with inferred and default fields marked. Example images are not under version control, run it with your own model if you want to see them.