---
name: comfyui_build_sprite_sheet_workflow
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: pure
signature: "def comfyui_build_sprite_sheet_workflow(subject: str, *, ref_image: str | None = None, pose_skeleton: str | None = None, ckpt_name: str = \"dreamshaper_8.safetensors\", char_lora: str | None = None, lora_strength: float = 1.0, controlnet_name: str = \"control_v11p_sd15_openpose_fp16.safetensors\", controlnet_strength: float = 0.55, controlnet_start: float = 0.0, controlnet_end: float = 0.8, transparent: bool = True, rembg_model: str = \"u2net\", weight: float = 0.75, negative: str = \"blurry, lowres, extra limbs, deformed\", width: int = 512, height: int = 768, steps: int = 24, cfg: float = 7.0, seed: int = 0, sampler_name: str = \"dpmpp_2m\", scheduler: str = \"karras\", filename_prefix: str = \"sprite\") -> dict"
description: "Construye el dict (API format) del workflow de UN sprite de personaje 2D: identidad (IPAdapter-FaceID si ref_image) + LoRA opcional + pose (ControlNet OpenPose con ControlNetApplyAdvanced end<1.0) + transparencia (Rembg). Compone comfyui_build_ipadapter_workflow / comfyui_build_txt2img_workflow + comfyui_inject_lora. Es UN frame; varias poses (misma seed) -> sprite sheet montado con comfyui_build_grid. Pura, sin red ni I/O. class_types verificados contra /object_info."
tags: [comfyui, ml, gamedev, gamedev-2d, sprite, character, faceid, openpose, workflow]
uses_functions: [comfyui_build_txt2img_workflow_py_ml, comfyui_build_ipadapter_workflow_py_ml, comfyui_inject_lora_py_ml]
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
params:
  - name: subject
    desc: "Descripcion del personaje (ej. 'a knight in silver armor'). Se completa con ', full body, game sprite, simple background'. No puede estar vacio."
  - name: ref_image
    desc: "Imagen de referencia de rostro en input/ del servidor. Si se pasa, usa IPAdapter-FaceID (identidad consistente). None = identidad solo por prompt + seed. keyword-only."
  - name: pose_skeleton
    desc: "Imagen de esqueleto OpenPose en input/ para fijar la pose via ControlNet. None = pose libre. keyword-only."
  - name: ckpt_name
    desc: "Checkpoint SD1.5 (FaceID + OpenPose solo instalados en SD1.5; default 'dreamshaper_8.safetensors'). keyword-only."
  - name: char_lora
    desc: "LoRA de personaje/estilo opcional en models/loras. keyword-only."
  - name: lora_strength
    desc: "Fuerza del char_lora sobre model y clip. keyword-only."
  - name: controlnet_name
    desc: "ControlNet OpenPose (default SD1.5). keyword-only."
  - name: controlnet_strength
    desc: "Fuerza del OpenPose (recomendado 0.55). keyword-only."
  - name: controlnet_start
    desc: "Inicio de aplicacion del OpenPose (fraccion 0..1). keyword-only."
  - name: controlnet_end
    desc: "Fin de aplicacion del OpenPose (end<1.0 deja libres los ultimos pasos para pelo/ropa). keyword-only."
  - name: transparent
    desc: "Si True inyecta Rembg para PNG con alpha. False = opaco. keyword-only."
  - name: rembg_model
    desc: "Modelo Rembg ('u2net' general, 'isnet-anime' para anime). keyword-only."
  - name: weight
    desc: "Peso del IPAdapter-FaceID (solo si ref_image). keyword-only."
  - name: negative
    desc: "Prompt negativo. keyword-only."
  - name: width
    desc: "Ancho en px (512). keyword-only."
  - name: height
    desc: "Alto en px (768, vertical, encuadra cuerpo entero). keyword-only."
  - name: steps
    desc: "Pasos del KSampler. keyword-only."
  - name: cfg
    desc: "CFG del KSampler. keyword-only."
  - name: seed
    desc: "Semilla del KSampler (fijar igual entre poses para identidad estable). keyword-only."
  - name: sampler_name
    desc: "Sampler del KSampler. keyword-only."
  - name: scheduler
    desc: "Scheduler del KSampler. keyword-only."
  - name: filename_prefix
    desc: "Prefijo del PNG en output/. keyword-only."
output: "dict en API format listo para comfyui_submit_workflow: base txt2img (o IPAdapter-FaceID si ref_image) + LoRA opcional + ControlNet OpenPose (si pose_skeleton, via ControlNetApplyAdvanced) + Rembg (si transparent). UN sprite; varias poses -> sprite sheet."
tested: true
tests: ["golden ref+pose+transparent: clases IPAdapterUnifiedLoaderFaceID/IPAdapterFaceID/ControlNetApplyAdvanced/Image Rembg; KSampler.positive/negative <- ControlNetApplyAdvanced; SaveImage <- Rembg; end_percent 0.8", "edge sin ref: sin IPAdapter, base txt2img", "edge transparent=False: sin Rembg, SaveImage <- VAEDecode", "edge char_lora: LoraLoader presente", "error subject vacio -> ValueError", "determinismo"]
test_file_path: "python/functions/ml/comfyui_build_sprite_sheet_workflow_test.py"
file_path: "python/functions/ml/comfyui_build_sprite_sheet_workflow.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_build_sprite_sheet_workflow import comfyui_build_sprite_sheet_workflow

# Un frame del turnaround: identidad (FaceID) + pose frontal + alpha.
wf = comfyui_build_sprite_sheet_workflow(
    "a knight in silver armor",
    ref_image="faceref.png",          # cara de referencia en input/
    pose_skeleton="pose_front.png",   # esqueleto OpenPose en input/
    transparent=True,
    seed=5,
)
# Sprite sheet completo: repetir cambiando pose_skeleton (front/side/back),
# MISMA seed para identidad estable, y montar los PNG con alpha:
#   comfyui_submit_workflow x N  ->  comfyui_build_grid(paths)
```

O lanzable directo con: `./fn run comfyui_build_sprite_sheet_workflow` (imprime nodos + class_types del ejemplo).

## Cuando usarla

Cuando generes personajes 2D para un juego con identidad consistente entre poses
(turnaround front/side/back, set de acciones). Fija `seed` y `ref_image` iguales
entre llamadas y varía `pose_skeleton` para mantener la misma cara. `transparent`
deja el sprite recortado (alpha) listo para el motor. Para el contact-sheet final,
monta los PNG resultantes con `comfyui_build_grid`.

## Gotchas

- **Solo SD1.5 hoy**: IPAdapter-FaceID y ControlNet-OpenPose están instalados solo
  en SD1.5. Usa `dreamshaper_8` u otro checkpoint SD1.5.
- **`ref_image`/`pose_skeleton` son nombres de archivos en el dir `input/` del
  servidor**, no rutas locales. Súbelas antes (LoadImage las lee de ahí).
- **Usa `ControlNetApplyAdvanced`** (no el legacy `ControlNetApply`): `end_percent`
  0.8 deja los últimos pasos libres para que pelo/ropa no queden aplastados contra
  el esqueleto.
- `Image Rembg` da matting binario (silueta sólida) — perfecto para personajes,
  pero NO para efectos translúcidos (humo/fuego): para eso, luma-as-alpha.
- Sin `char_lora` la consistencia de ropa/cuerpo entre vistas depende de
  prompt+seed (FaceID fija sobre todo la cara). Una LoRA de personaje la mejora.
- Función pura: no valida contra el server.
