CommunityImagegithub.com

character-refs

Generate character reference images for any story (novel adaptations, original stories, or single character designs). Describe a character in a paragraph, decompose it into layered fields, confirm, then generate a front full-body anchor image. All other views (headshot, 90° side, back, details, 45° headshot) reference only this anchor, with tiered image generation on demand.

What is character-refs?

Character-refs is a comprehensive AI skill designed for generating consistent character reference images from a text description. It is ideal for novel adaptations, original character design, or any story where visual consistency matters. The skill supports multiple image generation backends: Qwen Image via ComfyUI, codex's built-in image generation, OpenAI Images API (GPT Image 2), and custom commands. It enforces a strict hierarchy where a single front full-body image serves as the sole anchor; all other views (headshots, side, back, details) are derived from this anchor using modification instructions, not other derived images. This ensures that regenerating any single image does not cascade failures to others. The skill also provides a full workflow: intake and validation of character descriptions, asset management with versioning, tiered image generation (Tier 1: anchor; Tier 2: headshot, side, back; Tier 3: details; Tier 4: 45° headshot), regeneration of outdated images, and a double-clickable HTML report with expiration markers. It is zero-dependency (only requires Node.js) and stores all outputs as PNGs with embedded identifiers plus an asset.json file.

Works with✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/chulegexiaojiu/2dhuman-get-skill/tree/HEAD/skills/character-refs

Installed? Explore more Image skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

What does character-refs do?

Input a character description and output a set of character reference images—the character can come from anywhere: novel adaptations, your own stories, or a character you just thought of. One front full-body anchor image, and every other image references only this anchor. Use as reference images for video models (H3 / Wan / Seedance) or for human viewers.

{baseDir} = directory containing this file. Script {baseDir}/scripts/character-refs.mjs, zero dependencies, runs directly with node.

This is the only skill in the repository that actually generates images. Other skills only output prompts, because the model and art style can only be decided at generation time; this skill is that "moment"—the model is chosen by the user at Step 0, and the art style is selected when creating the character (default is anime, with a realistic preset available, and custom styles can also be used).

Core Rules (Read First, Then Act)

  • One and only one root per set: the front full-body anchor. Only this one is text-to-image; the rest are "anchor + one modification instruction." Hair, face, and collar details additionally reference a confirmed front-facing headshot. Images no longer reference each other— so if any image turns out bad, regenerate only that one without causing a chain reaction.

  • Tiered output, default to Tier 2:

    TierContentWhen to Generate
    1Front full-body (anchor)Always, first image
    2 (default)Front-facing headshot, 90° side (facing right), backGenerated by default
    3Detail images (up to 8): user-specified areas, most recognizable parts of the character; if none specified, default to hair / hair accessories, collar, cuffs, shoesWhen there are accessories or clothing details needing close-ups
    4 (not default)45° headshot (facing left)Only when explicitly requested by user

    H3 testing: providing only the front full-body gives correct clothing and back, but the face distorts; adding one headshot keeps the face most consistent. So default output includes Tier 2. For only the anchor (e.g., to check appearance first): gen <asset.json> --tier 1.

  • Model not locked: The main image and derived images can use different models; regenerating a single image can also switch models. Consistency comes from "same anchor," not from "same model" (tested: Qwen anchor + GPT derived, GPT anchor + Qwen derived, mixed within a group all align). Each image records its own model; mixed models in a group are only noted in the report.

  • Only three things are frozen: anchor file (sha256), art style snapshot, and text description fingerprint. If any changes, images depending on it are marked "expired"—only marked, not deleted, old versions are all retained.


Step 0 — Initial Setup (Once Per Machine)

node {baseDir}/scripts/character-refs.mjs config --show

If the output ends with "Missing: …", ask the user for these three things all at once:

  1. Model for main image (anchor) :
    • qwen — Qwen Image 2.1, via your own ComfyUI server. Fast (25–45s per image), precise dimensions, doesn't consume subscription quota. Requires COMFY_URL (optional COMFY_USER / COMFY_PASS), set in environment variables or a .env file
    • codex — codex's built-in image generation (GPT). No API key needed, but consumes ChatGPT subscription quota (Plus hits the limit around 25 images, recovers after a few hours), 80–160s per image
    • openai — OpenAI Images API, default gpt-image-2. Pay-as-you-go, requires OPENAI_API_KEY; can also point to a compatible API (--openai-base-url)
    • custom:<name> — user's own image generation command, see references/models.md
  2. Model for derived images: Defaults to the same as the main image. Can be different (e.g., anchor with GPT, derived with Qwen)
  3. Whether to pause after generating the anchor for human confirmation: Recommended yes—the anchor is the root of the whole set; if it's wrong, everything else is wasted. Choosing no proceeds automatically, with a note in the report "not human-confirmed"; anchors that fail the check gate are not automatically approved—must be reviewed and confirmed or regenerated
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 saved to {baseDir}/config.local.json (not in version control, not overwritten on reinstall). It will be used by default from then on; if the user says "switch to xx," rerun config. Do not print passwords or keys from .env—config --show already masks them.

Step 1 — One-Time Input

The user provides one paragraph describing the character—no need to ask item by item. You (the model) decompose it:

node {baseDir}/scripts/character-refs.mjs intake-template

Split the description into fields according to the template and write to <working directory>/<character-name>-intake.json. Rules (see references/intake.md):

  • lang should be the language the user spoke (Chinese zh, English en, Japanese ja are built-in). The confirmation table and report use this language; for other languages, first run ui-template <lang>, translate the printed English text item by item, and place the whole block into the intake's ui field
  • Mark user-specified items as stated, items you infer as inferred, and items filled by convention as default—mark them truthfully; the confirmation table uses this to show the user which items to check
  • Age, gender, and era cannot be inferred—ask the user. Only these three must not be invented; infer everything else without asking
  • en goes into prompts: always in English, no character names, no art style terms; text is for human reading, written in lang (old inputs with zh are still recognized)
  • Clothing is split into top / bottom—headshots use only the top; if both are included, the model will pull the camera away
  • Detail images are chosen per character: prioritize user-named areas; if none named, pick the most recognizable parts of that character, with four default slots as fallback; facial features are not part of detail images (the headshot already shows them clearly). See references/intake.md
node {baseDir}/scripts/character-refs.mjs intake-check <intake.json>

If there are errors, fix them until it passes. Once it passes, it prints a confirmation table—show it exactly as-is to the user, with inferred and default items marked. Proceed only after user confirms (or modifies and confirms).

Step 2 — Create Asset

node {baseDir}/scripts/character-refs.mjs new <intake.json> --out <output directory> [--look art-style]

Creates <output directory>/<character-name>/asset.json. The art style is snapped into the asset at this point; later changes to presets do not affect existing characters.

Art style: Use what the user specified, or default to anime if not specified. Characters in the same series must all use the same art style.

PresetName recognized by --look
Anime (default)动漫 卡通 anime cartoon 二次元 アニメ
Realistic photo写实 realistic photo 真人

looks lists all presets. For custom styles (e.g., Chinese ink, American comic): look-template <closest preset> > my-look.json, then edit style (medium, lines), clean (background and lighting, must keep white background), neg (negative prompt—for a drawn style, remove anime illustration from the negative prompts), and for drawn art styles also change medium to drawn, then use --look my-look.json. Custom styles have not been tested; generate the anchor first to check.

To change the art style of an existing character:

node {baseDir}/scripts/character-refs.mjs restyle <asset.json> --look anime

After the change, all existing images (including the anchor) are marked expired; old images are kept, and regeneration starts from the anchor.

Step 3 — Generate Default Tier 2

node {baseDir}/scripts/character-refs.mjs gen <asset.json>

Without specifying view or tier, it fills in the first and second tiers—images not yet generated or expired.

If confirmation is configured: this command stops after generating just the anchor. Show the image to the user (via report or by directly opening the PNG). If the user says it's okay, then:

node {baseDir}/scripts/character-refs.mjs confirm <asset.json>
node {baseDir}/scripts/character-refs.mjs gen <asset.json>      # Same command; continues with headshot, side, back

If unsatisfied, rerun gen <asset.json> front-full (generates v2; can use --seed to change seed, --model to change model), or go back to Step 1 to modify the description. If the anchor is not confirmed, derived images are rejected. If configured to not confirm, all four images are generated in one command.

Step 4 — Upgrade Tiers on Demand

node {baseDir}/scripts/character-refs.mjs gen <asset.json> --tier 3 --reason "E03 has hair ornament and collar close-ups"

--reason documents why the tier was upgraded, recorded in the asset. You can also generate only specific images: gen <asset.json> detail-hair detail-neck.

Step 5 — Check and Regenerate

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, ratio matches expectations, ≤20MB, background sufficiently white), then checks for expiration. Exits with code 1 on failure. For images that failed the gate or are expired, regenerate only that specific image:

node {baseDir}/scripts/character-refs.mjs gen <asset.json> detail-neck --model codex

In the gate, "background sufficiently white" checks the four edges for full-body images, only the top edge and upper sides for headshots (since the body naturally touches the bottom), and is skipped for detail images with a clear note. The gate cannot check appearance, angle, or framing—always view the images after generation; these are eyeball checks.

Step 6 — Report

node {baseDir}/scripts/character-refs.mjs render <asset.json>... --out <output directory>/character-refs.html

Multiple characters can be combined. The language defaults to the asset's lang; switch temporarily with --lang en; for non-built-in languages without a ui field, pass translated text via --ui <file>, if missing it will error directly rather than outputting a half-translated report. The UI text changes language, but character descriptions remain in their original language. Tier 1 shows only one card; from Tier 2 onward, it uses a design sheet layout (left: headshot, right top: front/side/back, right bottom: detail strip), detail images are hidden by default, with a one-click toggle on the page. Expired images have a red border; a legend at the top of the page explains, and hovering over a red-bordered image shows the reason; mixed models are indicated.

Step 7 — Report Back

In one sentence: which images were generated, which model was used, and the report path. Point out any images that failed the gate or are expired. Without looking at the images, do not say "Looks great."


Output Structure

<output directory>/
├── character-refs.html          ← output of render
└── <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 references to which images it was based on—so even if a PNG is copied elsewhere, its origin can be traced. The structure of asset.json is documented in references/schema.md.

Boundaries

  • Only two art styles have been tested in practice: realistic and anime (both run through Qwen with all four tiers); results for custom styles depend on how they are written—generate the anchor first to evaluate
  • Detail images only cover items physically attached to the character (hair ornaments, collar, cuffs, shoes); props belong to novel-art
  • Known limitations: small scars tend to appear as fresh wounds; large-area details like double-breasted jackets are framed too far away; Qwen's collar details may render rough fabric as velvet (regenerate that image with codex); skin exposed in Qwen detail images may appear older (the prompt already includes an age statement, effective for the 16-year-old test case but not extensively validated)
  • The openai adapter has only been tested with a local fake server for request formatting—never run with a real API key; generate one anchor image first to verify
  • The same set of prompts has been tested on both Qwen and GPT; results for custom models depend on that model

Self-Test

node {baseDir}/scripts/selftest.mjs

230 assertions, no model calls, no quota consumption: PNG identifier read/write, check gate, input validation, multilingual confirmation table and report, art style presets and restyling, prompts, expiration propagation, request format for image adapters (Qwen and GPT Image using local fake servers), and a full workflow using custom commands. Run this after modifying any script.

Built-In Examples

{baseDir}/examples/阿禾-description.txt is a one-paragraph user-style description (a 1920s Jiangnan tea-picking girl), 阿禾-intake.json is the decomposed input with inferred and default fields marked. Sample images are not in version control; to see them, run through your own model.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Related Skills