| name | kakao-emoticon-forge |
| description | Turn a known cartoon/comic character into a KakaoTalk-quality emoticon set (360x360 transparent, animated). Extracts a clean single-character reference, then generates held-cel sprite sheets with gpt-image-2 (body held, one part moves), splits/keys/stabilizes them, and compiles to KakaoTalk spec + GIF + PNG. Use when "이모티콘 만들어줘", "카카오 이모티콘", "캐릭터 이모티콘화", "움직이는 이모티콘", "emoticon set", "kakaotalk sticker set", "animate my character", "sprite emoticon". Do NOT use for one-off single stickers/messengers other than Kakao (use sticker-forge `stickerforge make`), plain image generation (use gpt-image-2), a pose sheet meant for dance/video (use pose-sheet-to-animation), or video (use video-producer). |
KakaoTalk Emoticon Forge
Known character (blog comic, mascot, IP you own) -> a submittable KakaoTalk
emoticon set. Quality comes from held-cel limited animation: the body is a
fixed copied layer and only one named part moves per expression, exactly how a
human animator works, so the character does not jitter.
Engine lives in the sticker-forge submodule (thin harness, fat skill).
Python: sticker-forge/.venv/bin/python. Requires OPENAI_API_KEY (in repo .env).
KakaoTalk submission spec (research-backed)
24종 제안: 3종 이상 움직이는 GIF (24프레임 이하), 나머지 정지 PNG 가능. 전부 360x360px,
배경 투명, 개당 2MB 이하, 72dpi/RGB. 메인 이미지 1 + 썸네일 1. 심사 4기준: 창의성/완성도/
시장성/적합성 (통과율 ~3%). 금지: 저작권 침해(연예인/브랜드/타사 캐릭터) — 본인 IP만.
자세한 기획: sticker-forge/docs/kakao-emoticon-guide.md.
Pipeline (4 steps)
1. Identify the character(s)
Blog comics live as multi-panel strips. View a few strips (Read the image), pick
the recurring, expressive characters (mascots/robots animate best). For ThakiCloud:
thakicloud.github.io/assets/images/posts/comics/*/strip.{png,webp}.
2. Extract a clean single-character reference (gpt-image-2 edit)
Crop a panel where the target is prominent, then isolate + redraw it clean:
Verify the reference visually (identity must match). Save as <name>_ref.png.
3. Generate the emoticon set (held-cel + stabilize)
cd sticker-forge
OPENAI_API_KEY=... .venv/bin/stickerforge emoticon \
-r outputs/.../<name>_ref.png --name <name> --out outputs/emoticon/<name>
or in Python:
from stickerforge.emoticon import make_emoticons, KAKAO_EXPRESSIONS
make_emoticons("<name>_ref.png", "<name>", "out/<name>")
make_emoticons(..., expressions=KAKAO_EXPRESSIONS)
4. Outputs
out/<name>/{webp,gif,png}/<expr>.* + manifest.json. WebP/GIF are animated
(KakaoTalk spec, gate-checked); PNG is a representative static frame for the
non-animated slots. Build a contact sheet and open it to review.
How the four jitter/clip fixes stack (the core idea)
Getting a clean set took four layers that all live in code now. Each was a real
failure fixed in production; keep all four:
- Held cel (identity + no body redraw) —
build_held_cel_prompt emits a JSON
spec declaring the body a fixed layer copied into all 16 cells, only
Expression.part moving (wave=arm, cry=eyes/tears, walk=legs). Stops the
whole-body redraw wobble.
- Even grid + small character + margin (clean cuts at generation) — the same
JSON demands a strict evenly-spaced grid AND a SMALL character (central 55-60%
of the cell) with a thick empty margin on every side, plus a
size_lock clause
("same size in every cell, as if one sprite is stamped"). Without this, gpt-image-2
draws characters edge-to-edge and slicing cuts feet / bleeds a neighbor's foot in.
- Adaptive gutter split —
sprite.split_sheet(adaptive=True) snaps each cut to
the emptiest row/col (ink-profile minimum) so boundaries land in the gutter, not
through a character. Equal-grid slicing alone is not enough.
- Size-lock registration —
stabilize.stabilize_clip scales every frame so its
BODY CORE height (_core_height, wide rows only, ignoring thin raised arms) is a
fixed fraction of the canvas, then plants feet on a common baseline, centered.
This is the deterministic guarantee of identical size; the prompt alone can't.
Raw sheets are saved to out/<name>/sheets/<expr>.png, so you can re-split and
re-stabilize (steps 3-4) for FREE without paying to regenerate.
gotchas (learned in production)
- ❌ Free "redraw each cell" prompts jitter (body redrawn every frame). Use held-cel.
- ❌ Characters drawn edge-to-edge -> equal-grid slicing cuts feet and bleeds a foot
from the cell above. Fix at generation (small char + margin) AND split (adaptive gutter).
- ❌ Residual size wobble -> a prompt "same size" is not enough; the size-lock in
stabilize_clip (core-mass height) is what actually makes every frame identical.
- ❌ gpt-image-2 rejects
background="transparent" for this model -> generate on
white, key it out (generation.base.key_background, border flood-fill).
- ❌ System ffmpeg can break (jpeg-xl/libjxl); WebM uses the bundled imageio-ffmpeg.
- Cost: ~$0.05-0.17 per high-quality sheet; 24 expr x 3 chars is real spend/time.
Start with core 10 and one character; re-split saved sheets for free when tuning.
Verify
- Offline (no cost):
sticker-forge/.venv/bin/python -m pytest tests/test_emoticon.py tests/test_sprite.py tests/test_stabilize.py -q covers the expression set, the
held-cel JSON spec, sheet split/key/assemble, and frame stabilization.
- Live smoke: run one character, core 10, then build a contact sheet + onion-skin
overlay (mean of frames) — a tight silhouette means frames are registered; a
spread one means jitter remains. Review before scaling to the full 24 x N chars.
References
- Engine:
sticker-forge/src/stickerforge/emoticon.py, motion/sprite.py, motion/stabilize.py.
- Spec/plan:
sticker-forge/docs/kakao-emoticon-guide.md.
- Related skills:
gpt-image-2 (reference extraction), sticker-forge CLI (stickerforge make).