Chibi Sticker Sheet logo

Chibi Sticker Sheet

Community
Innei
chibi-sticker-sheet

Use when generating a LINE/WeChat-style chibi sticker sheet (4x8 grid, 32 expressions) from an anime character reference image via Gemini, including transparent PNG output and individual cell slicing.

Overview

PublisherInnei
RepositorySKILL
Skill namechibi-sticker-sheet
Stars
81
Forks
2
Bundled files
5
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 5 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by Innei on GitHub. Read the source before you install it.

Installation

Install the Chibi Sticker Sheet AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/Innei/SKILL.git /tmp/SKILL
mkdir -p .claude/skills
cp -r /tmp/SKILL/skills/content/chibi-sticker-sheet .claude/skills/chibi-sticker-sheet
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Chibi Sticker Sheet in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Chibi Sticker Sheet on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Chibi Sticker Sheet is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Chibi Sticker Sheet

Generate a 4×8 chibi sticker sheet from a character reference image with Gemini, then key out the white background and slice into 32 individual transparent PNGs.

Scope

  • Input: 1 character reference PNG + a list of 32 expressions
  • Output: sheet_white.png, sheet_transparent.png, cells/*.png (512×512 each)
  • Requires: any one of GOOGLE_AI_STUDIO_API_KEY / GEMINI_API_KEY / GOOGLE_API_KEY (AI Studio) or VERTEX_AI_KEY (Vertex Express) or GOOGLE_GENAI_USE_VERTEXAI=true + GOOGLE_CLOUD_PROJECT/LOCATION (Vertex ADC); Python + uv; model gemini-3.1-flash-image-preview
  • Not covered: per-cell text overlays; use PIL ImageDraw post-hoc for captions

Key Finding: Double-Matte Fails

The natural "white bg + black bg → α extraction" requires pixel-aligned foregrounds. Gemini ignores bg-change instructions in image-to-image edits and returns the same image. Use edge flood-fill keying instead (see scripts/key_alpha.py).

Inputs

VariableMeaning
CHAR_REFAbsolute path to character reference PNG
EXPRESSIONSList of 32 strings, action-first (e.g. "waterfall tears streaming down both cheeks")
OUT_DIROutput directory

Workflow

text
[1] Generate white-bg 4×8 sheet (two Gemini calls, each 4×4)
      -> gemini-3.1-flash-image-preview, image-to-image with CHAR_REF
      -> Call A: expressions 1–16  -> sheet_white_a.png
      -> Call B: expressions 17–32 -> sheet_white_b.png
      -> Stitch A and B vertically -> sheet_white.png

[2] Key out background
      -> scripts/key_alpha.py: edge flood-fill via scipy.ndimage.label
      -> save sheet_transparent.png

[3] Slice 4×8 grid
      -> scripts/key_alpha.py: min(cw, ch) square crop, centered per cell
      -> save cells/01_*.png … cells/32_*.png (512×512)

Why Two Calls

Gemini's image output resolution caps around 1024×1024. A single 4×8 canvas would squash each cell to ~128×256px — too small for clean chibi art. Generating two 4×4 sheets and stitching keeps per-cell resolution at the same quality as 16-sticker sets.

Prompt Structure

Two calls (Call A: expressions 1–16, Call B: expressions 17–32), each image-to-image with the same character reference.

Use identical art-style and character-lock text for both calls to ensure visual consistency.

Generate a 4x4 grid sticker sheet of 16 chibi stickers of the same character
from the reference image. Seamless pure white (#ffffff) background; no grid
lines, no cell borders, no text, no captions. Stickers evenly spaced.

Art style: LINE / WeChat Japanese chibi sticker, extreme super-deformed
2-head body ratio, oversized round head, tiny stubby body, thick uniform
bold black ink outline, flat cel shading, warm creamy pastel palette, two
large round pink cheek blush dots on every face, large round eyes with a
single bright white highlight, mochi chibi aesthetic.

Character lock (must match in every single cell):
[list each attribute: hair color/style, accessories, eye color, outfit details]

16 expressions, left-to-right top-to-bottom:
1. <action-first description>
...
16. <action-first description>

Run the same prompt template a second time with expressions 17–32 substituted in. Then stitch:

python
from PIL import Image

a = Image.open("sheet_white_a.png")
b = Image.open("sheet_white_b.png")
# Resize b to match a's width if they differ (Gemini output dims can vary)
if b.width != a.width:
    b = b.resize((a.width, int(b.height * a.width / b.width)), Image.LANCZOS)
combined = Image.new("RGB", (a.width, a.height + b.height), (255, 255, 255))
combined.paste(a, (0, 0))
combined.paste(b, (0, a.height))
combined.save("sheet_white.png")

Prompting Rules

  • Lock first, change second. Open with "Character lock" before anything changes.
  • Action-first expressions. "waterfall tears streaming" beats "sad". Include a visible prop or gesture.
  • Be concise. Overlong prompts (>300 words) trigger MALFORMED_FUNCTION_CALL. No negative-list clauses (must NOT).
  • No in-image text. Gemini cannot reliably render Chinese/Japanese — add captions via PIL post-hoc.
  • Style vocabulary that works: super-deformed 2-head ratio · thick uniform bold black ink outline · flat cel shading · mochi chibi · large round pink cheek blush dots
  • Character attributes to lock: hair color, hair length/style, ALL accessories (花饰 must name species: pink cherry blossom sakura flower hair ornament), eye color, every garment piece.

Expression Diversity Rules

Gemini tends to reuse the same pose for cells that share a column, especially cells 9 and 13 (column-1 of rows 3–4) and the equivalent pairs in the second sheet (25 and 29). Prevent duplicates:

  1. Span all six emotional axes across the 32 slots — no axis should appear more than 6 times:

    AxisExample actions
    Joy / excitementraised fist, jumping, sparkle eyes
    Sadness / cryingwaterfall tears, wilting head, tissues
    Anger / frustrationpuffed cheeks, steam from head, finger-point
    Surprise / shockwide-O mouth, hands-on-cheeks, dropped jaw
    Shy / embarrassedhands over red face, hiding behind sleeves
    Calm / smugarms crossed, side-eye, tea-sipping
  2. Assign a distinct body verb to every cell. If two cells share the same verb (e.g., both "crying"), Gemini collapses them. Make verbs orthogonal: waterfall-tears arms-spreadsingle-teardrop hands-clasped.

  3. Critical column-1 pairs: 9 & 13, and 25 & 29 must each differ in both emotion axis AND body posture. Write each pair side by side before finalising and check they are visually distinguishable.

  4. Vary facing direction and limb action across rows. Cells in the same column naturally echo each other; counteract by alternating pose direction (facing left vs. right) or prop presence.

  5. No expression from Call A (1–16) should be repeated in Call B (17–32). Cross-check verbs before submitting the second prompt.

Reference 32-slot layout (copy and customize):

Call A (sheet 1, expressions 1–16):

1.  arms raised, jumping for joy, sparkle eyes
2.  waterfall tears streaming, arms limp at sides
3.  puffed cheeks, steam wisps from head, fists clenched
4.  wide-O mouth shock, both hands on cheeks Home-Alone pose
5.  smug smile, arms crossed, eyes half-lidded
6.  shy embarrassment, hands pressed together, deep blush
7.  thumbs-up grin, winking one eye
8.  single teardrop rolling, trembling lip, hands clasped
9.  hyper-excited wave, leaning forward, mouth wide open    ← must differ from #13
10. exhausted slumped, sweat drop, drooping eyes
11. index finger raised, lecture pose, tiny smile
12. heart eyes, both hands framing face, rosy cheeks
13. angry stomp, foot raised, fist shaking at sky           ← must differ from #9
14. sleeping ZZZ, head tilted, eyes closed
15. nervous laugh, hand behind head, eye twitch
16. victory peace-sign, tongue out, confetti burst

Call B (sheet 2, expressions 17–32):

17. cheering both fists raised, eyes glittering, mouth wide
18. sobbing face buried in hands, shoulders shaking
19. furious vein-pop, pointing finger, leaning in
20. startled leap, arms flailing outward, pupils tiny
21. lovesick float, dreamy spiral eyes, hearts around head
22. pouty sulk, arms crossed, cheeks puffed, eyes averted
23. excited run, legs spinning wheel, sweat drops flying
24. relieved sigh, hand on chest, eyes closed in relief
25. cheerful skip, one leg up, waving hello              ← must differ from #29
26. defeated head-desk slump, tiny sweat rivers
27. determined fist-pump, one eye closed, cape flutter
28. panicked sprint, eyes wide, papers scattering
29. grumpy arms-crossed side-eye, tapping foot impatiently ← must differ from #25
30. yawning stretch, arms out, eyes teary from yawn
31. embarrassed covering ears, blushing furiously, hunched
32. triumphant pose, foot on imaginary podium, sparkle burst

Grid Slicing: Auto-Detect Boundaries

Gemini does not divide the canvas into equal cells. Row/column heights vary (e.g., top row 550px vs. bottom row 480px). Hard image_size / 4 cuts cause sticker overflow into adjacent cells.

After stitching the two 4×4 sheets into one 4×8 canvas, run _find_cuts() on the combined image. Pass n_rows=8, n_cols=4.

Fix: compute per-row and per-column dark-outline occupancy profiles instead of white-fraction profiles. True inter-cell gaps have zero or near-zero dark pixels because the black outline disappears entirely in the white gutter, while white hair or clothing inside a sticker still leaves some outline pixels somewhere on that same row/column.

python
dist = np.max(np.abs(rgb.astype(np.int16) - 255), axis=2)  # 0 = white
dark = binary_dilation(dist > OUTLINE_THRESH, iterations=1)
row_profile = dark.sum(axis=1)          # dark outline count per row
col_profile = dark.sum(axis=0)          # dark outline count per col
# find contiguous near-zero-dark runs, then select the deepest valleys
# under broad cell-size sanity bounds instead of equal-spacing fallback

If a gutter is noisy and no exact zero-dark run survives, fall back to the lowest dark-count valleys in the smoothed profile. Cells are saved as cells/01_*.png … cells/32_*.png (512×512). See scripts/key_alpha.py: _find_cuts().

Alpha Keying: Edge Flood-Fill

See scripts/key_alpha.py. Core algorithm:

python
# dist[y,x] = max channel delta from pure white (0 = white, 255 = farthest)
dist = np.max(np.abs(rgb.astype(np.int16) - 255), axis=2)
near_white = dist < WHITE_TOL          # WHITE_TOL = 28

lbl, _ = label(near_white)            # connected components
edge_ids = {lbl[0,:], lbl[-1,:], lbl[:,0], lbl[:,-1]} - {0}  # border-touching
bg_mask = np.isin(lbl, list(edge_ids))

alpha = np.where(bg_mask, 0, 255).astype(np.uint8)
# feather boundary: GaussianBlur(radius=1.2) on alpha channel

Interior white pixels (shirt, skin highlights) are enclosed by foreground and never reach the border — they stay opaque.

White clothing leak: if the outline has 1–2 px gaps, the flood leaks through into white fabric. Fix: dilate dark outline pixels (dist > OUTLINE_THRESH=150) by OUTLINE_DILATE=2 iterations before flood-fill to plug gaps.

Retry Pattern

Gemini 3 image models have three transient failure modes:

SymptomAction
503 UNAVAILABLE / 429 RESOURCE_EXHAUSTEDExponential back-off (2^attempt * 5s), 6 retries
FinishReason.MALFORMED_FUNCTION_CALLShorten/simplify prompt; remove negative clauses
resp.parts is None, only text returnedRetry; tighten the lock clause

See scripts/generate.py for the full retry wrapper.

Cross-Sheet Consistency (32-sticker specific)

Two separate Gemini calls will often produce slightly different rendering — brightness, line weight, palette temperature, or shading style may drift. Control it with the following steps:

1. Lock the style prompt byte-for-byte

Copy the exact art-style paragraph and character-lock paragraph from Call A into Call B without any edits. Even synonym substitutions (circular vs round) can shift Gemini's style.

2. Submit Call B with the sheet_white_a.png as additional reference

Pass sheet_white_a.png alongside CHAR_REF as the image input for Call B. Gemini will use it as a visual anchor. Example (google-generativeai SDK):

python
parts = [
    PIL_to_part(char_ref_img),
    PIL_to_part(sheet_a_img),  # style anchor
    types.Part(text=prompt_b),
]

3. Post-process for brightness/contrast parity

After stitching, compute the mean luminance of the top half (sheet A cells) and bottom half (sheet B cells). If they differ by more than 8 luma units, apply a PIL.ImageEnhance.Brightness correction to the dimmer half before writing sheet_white.png.

python
from PIL import Image, ImageEnhance
import numpy as np

def mean_luma(img_crop):
    arr = np.asarray(img_crop.convert("L")).astype(float)
    return arr.mean()

combined = Image.open("sheet_white.png")
h = combined.height // 2
luma_a = mean_luma(combined.crop((0, 0, combined.width, h)))
luma_b = mean_luma(combined.crop((0, h, combined.width, combined.height)))
if abs(luma_a - luma_b) > 8:
    # brighten or darken the bottom half
    factor = luma_a / luma_b
    bottom = combined.crop((0, h, combined.width, combined.height))
    bottom = ImageEnhance.Brightness(bottom).enhance(factor)
    combined.paste(bottom, (0, h))
    combined.save("sheet_white.png")

4. Visual QC before alpha keying

Open sheet_white.png and scan for:

  • Line weight mismatch (bottom half lines thinner/thicker)
  • Palette temperature shift (warmer/cooler tones)
  • Hair color drift across the boundary row

If drift is visible, re-run Call B with a slightly adjusted prompt (e.g., add "same warm creamy pastel palette as reference sheet") and a fresh seed. Repeat QC.

5. Common cross-sheet failure modes

SymptomCauseFix
Sheet B looks sketch-like, less shadedGemini skipped cel-shadingAdd "flat cel shading, no sketch lines" to Call B prompt
Hair color changes between row 4 and row 5Character lock didn't persistRepeat exact hex color code in Call B character lock
Sheet B overall darkerDifferent generation contextApply brightness correction (step 3)
Outline weight visibly thinner in sheet BGemini style varianceAdd "thick uniform bold black ink outline, same line weight as reference"

Common Mistakes

MistakeFix
Vague accessories (red ribbon)Spell out species/shape (pink sakura flower, 5 petals)
Uniform expressions (all sad-variants)Mix action verbs: raised fist, palm-push, tilted head, waterfall tears, thumbs-up
Cells 9 and 13 look identicalThey share column-1; assign different emotion axis and body posture to each — see Expression Diversity Rules
Grid lines appear in outputAdd seamless pure white, no grid lines, no cell borders to prompt
Hair color drifts across cellsRepeat exact color spec as first item in "character lock"
image.size AttributeErrorpart.as_image() returns genai Image, not PIL; convert via Image.open(io.BytesIO(img.image_bytes))
Adjacent sticker bleeds into cellGemini grid is uneven; use _find_cuts() dark-profile detection, not image_size // 4
White clothing becomes transparentOutline gaps let flood reach fabric; set OUTLINE_DILATE=2 to dilate outline before flood-fill

WeChat Submission Extras

Each sticker set needs three additional assets. Generate with scripts/generate_extras.py:

bash
uv run generate_extras.py <sticker_dir> <char_ref_image> "<theme hint>"
AssetSpecHow produced
banner.png750×400 PNG, colorful bg, no textGemini image-to-image, 16:9 → center-crop
cover.png240×240 transparent PNG, half/full bodyCell 07 (thumbs-up) resized with PIL
icon.png50×50 transparent PNG, head shotCell 07 full cell resized (no crop — chibi proportions fit naturally)

Theme hints by set:

  • Snow/winter → "snowy winter wonderland"
  • Autumn ginkgo → "golden autumn ginkgo forest"
  • Summer sailor → "sunny summer beach ocean waves"
  • Spring school → "cherry blossom spring school campus"
  • Kimono/plum → "red plum blossom Japanese garden"

WeChat rules:

  • Banner: colorful background only — no white, no transparent; no text; story-rich scene
  • Cover: transparent bg; no white outline; avoid over-cropping (half/full body preferred)
  • Icon: transparent bg; head-only, no square border; must differ across sets

Multi-Platform Banner

scripts/generate_banner.py produces a standalone banner for any registered platform — independent of the WeChat extras flow. WeChat itself is one preset; Twitter/X is another. New platforms are added by appending to the PLATFORMS dict.

bash
uv run generate_banner.py <char_ref> "<theme>" <out_path> --platform twitter
PlatformSizeGemini ARCrop
wechat750×40016:9minimal (~6% vertical trim)
twitter1500×50016:9aggressive (middle 56% only)

Each preset is a Platform(width, height, aspect_ratio, composition_hint). The hint is appended to the prompt and is the place to encode platform-specific safe-zone layout (avatar overlay, mobile crop, letterbox bars). WeChat needs no hint; Twitter needs explicit letterbox awareness — see below.

Letterbox Lesson (Extreme Aspect Ratios)

Gemini 3 image preview only supports a fixed aspect-ratio menu (1:1, 4:3, 3:4, 16:9, 9:16). For ratios more extreme than 16:9 (Twitter 3:1, LinkedIn 4:1) we generate at 16:9 then center-crop the middle band. The naive prompt strategy fails in two opposite ways:

Prompt told GeminiFailure mode
"Subjects must fit inside the 22%–78% band"Gemini adds safety margin → chibis end up at ~35%–65% → tiny in final frame
"Subjects must fill the canvas vertically"Gemini puts hair at 5% of source canvas → heads land in the cropped-off top 22% → decapitation

The fix is to tell Gemini that the top/bottom 22% are off-screen letterbox bars (showing only background overflow), and place subjects in the middle 25%–75% band of the source canvas. This produces full vertical fill in the final visible frame plus a 3% safety margin against the crop line on each side.

The Twitter preset's composition_hint encodes this verbatim. When adding a new extreme-aspect platform (e.g. LinkedIn 4:1 → middle 44% of 16:9 source), compute the letterbox percentages from the math and follow the same structure.

Verification

bash
# α channel: expect ~30-40% transparent pixels for a sticker sheet
python -c "
from PIL import Image; import numpy as np
a = np.asarray(Image.open('sheet_transparent.png').convert('RGBA'))[...,3]
print('min', a.min(), 'max', a.max(), 'transparent%', (a<10).mean()*100)
"

# Cell sizes must all be square
ls -la cells/*.png | awk '{print $5, $9}' | head

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Chibi Sticker Sheet AI skill do?

Use when generating a LINE/WeChat-style chibi sticker sheet (4x8 grid, 32 expressions) from an anime character reference image via Gemini, including transparent PNG output and individual cell slicing.

Why use Chibi Sticker Sheet on TypingMind?

Because you install it once and use it with any model. Chibi Sticker Sheet is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Chibi Sticker Sheet in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/Innei/SKILL/tree/main/skills/content/chibi-sticker-sheet. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Chibi Sticker Sheet?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Chibi Sticker Sheet?

As many as you like. As long as a model supports skills, you can use Chibi Sticker Sheet with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Chibi Sticker Sheet AI skill free?

It is published on GitHub by Innei. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇