Imported from nilbuild/page-mascot (
skills/page-mascot/SKILL.md) via skills.sh. Install upstream withnpx skills add nilbuild/page-mascot --skill page-mascot. Copyright stays with the author.
Mascot
Turns "put a fox on my page" or "draw me a chibi otter" into a working <Mascot />.
Each character is two 3×3 sprite sheets: nine head directions, and nine expressions. The
component swaps between them by moving background-position, so there is no per-frame
JavaScript and no animation library. A character is its two files -- there is no registry
and nothing has to know its name.
Which route
- The user names a character, or would take one that exists -- Use one that is drawn. No Python, no API key, works in any React project. This is the common case.
- The user wants something new, or their own likeness -- Draw a new one, then finish with Put it on the page.
Both routes end in Put it on the page. Do not stop at files and a snippet.
Use one that is drawn
-
Pick.
reference/characters.mdlists the fifty-two. If the user did not name one, pick the one that fits the site, and say which you picked and why. -
Fetch the two sheets into wherever this project serves static files --
public/for Next, Vite, CRA and Astro,static/for SvelteKit -- or next to the component to import them:mkdir -p public/mascots curl -fsSL -o public/mascots/fox-directions.webp https://koboyo.com/page-mascot/mascots/fox-directions.webp curl -fsSL -o public/mascots/fox-reactions.webp https://koboyo.com/page-mascot/mascots/fox-reactions.webp -
Put it on the page (below).
Put it on the page
Install the component. Use whichever package manager the lockfile points at:
npm i page-mascot, pnpm add page-mascot, yarn add page-mascot, or
bun add page-mascot. If this is not a package-managed React project, copy mascot.tsx
from beside this SKILL.md into the project instead -- it is one file, needs only React,
and uses inline styles.
Render it:
import { Mascot } from 'page-mascot'
<Mascot
directions="/mascots/fox-directions.webp"
reactions="/mascots/fox-reactions.webp"
/>
directions and reactions are the served paths of the two sheets, or imported images.
Both required. The other props are size (default 140), label (what a screen reader
calls it) and className.
Put it where the user asked. If they did not say, the top of the page -- the header or hero, above or beside the title, which is where a head that watches the cursor reads best. Edit the actual component; then tell the user what you changed and where.
Draw a new one
First, work out how you will draw
Two sheets have to be drawn per character, and agents differ in whether they can do that themselves. Check in this order:
- You have a built-in image generation tool (Codex does). Use it directly -- follow Drawing it yourself below. Find out first whether it can return a transparent background, because that decides which prompt you send: with the option, ask for transparency and set it; without one, draw on a solid key colour and remove it afterwards. Both are written out under Transparency.
OPENAI_API_KEYis set. Use the API path, which needs no image tool at all -- follow Drawing through the API. This is the route for Claude Code, which has no image tool.generate.pyasks for transparency through the API'sbackgroundparameter and falls back to the key route on its own if no alpha comes back.- Neither. Say plainly that drawing needs either an agent that can generate images or
an
OPENAI_API_KEY, and offer two alternatives: one of the fifty-two drawn characters (Use one that is drawn), or the manual route inreference/prompts.md, where the user pastes the prompts into a chat UI themselves. Do not try to drive a web UI in a browser to work around it: it depends on the page's markup, needs a logged-in session, and gets rate-limited part way through a set.
Either way, the build needs Python 3:
python3 -c "import PIL, numpy, scipy" 2>&1
If that fails: pip install -r <skill-dir>/scripts/requirements.txt. Stop and say so
rather than working around it.
<skill-dir> everywhere below is the folder holding this SKILL.md -- the skill is
usually installed outside the project, so use its full path. Run every command from the
project root: the scripts read source sheets from characters/<name>/ and write the built
atlases to public/mascots/ under the current directory. Pass --dest static/mascots (or
wherever this project serves static files) when public/ is not it.
Drawing through the API
One command generates, screens, builds, verifies and retries:
python3 <skill-dir>/scripts/mascot.py fox --describe "a chibi fox with warm orange fur, a cream muzzle and dark ear tips"
Add --style riso for a different look, or --reference ~/photo.jpg to redraw someone.
--only reactions redraws just the expressions sheet and keeps the directions sheet.
If the model returns no alpha channel, it redraws that sheet on a solid key colour and
removes it, which costs one extra image; --key green forces that route from the start.
It needs pip install openai alongside the packages above. The image model is pinned in
generate.py; set MASCOT_IMAGE_MODEL to use a different one.
Drawing it yourself
1. Draw the directions sheet. Use your image generation tool with the DIRECTIONS
prompt from reference/prompts.md, substituting the character description. If your tool
cannot return a transparent background, send the key variant of that prompt instead and
turn the option off -- see Transparency below, and send the same variant for both
sheets. Save it to characters/<name>/directions.png.
2. Draw the expressions sheet. Use the EXPRESSIONS prompt, and pass the directions
sheet you just made as a reference image. Save it to characters/<name>/reactions.png.
3. Build and verify.
python3 <skill-dir>/scripts/mascot.py <name> --skip-generate
4. Act on what it says. It either finishes and tells you the character is ready, or it names which of the two sheets is at fault. Redraw that one sheet and run step 3 again. Give up after two attempts and change the description instead -- past that the art is the problem, not the build.
Run the commands yourself. Do not print them for the user to copy.
Check the head turns point the right way
The scripts measure everything except which direction the character is actually looking.
Before you hand a character over, crop the left cell (middle row, first column) and the
right cell (middle row, third column) of the directions sheet and look at them: the left
one must face the viewer's left.
A sheet occasionally comes back mirrored, and it scores perfectly on every check while
looking away from the cursor on the page. Do not regenerate it -- the art is right and only
the cell order is wrong. reference/design-rules.md has the three-line column swap that
fixes it.
Transparency is the thing that most often goes wrong
The sheets must end up as PNGs with a real alpha channel, and the prompt cannot get
you one. Transparency is a setting on the image call -- background: "transparent" on
the OpenAI API, the equivalent option on a built-in tool -- not something a model does
because it was asked in words. Measured over fifteen sheets on gpt-image-2.5: with the
option set, every sheet came back with real alpha whether or not the prompt mentioned
transparency at all; without it, every sheet came back opaque with a painted
grey-and-white checkerboard behind the character. That includes a prompt demanding "PNG
WITH A REAL ALPHA CHANNEL... not white" in capitals. Unable to emit alpha, the model draws
the pattern that stands for it.
So there are two prompts, and which one you send depends on the tool, not on the character:
-
The tool can return a transparent background. Set that option, and use the prompts in
reference/prompts.mdas they are. -
It cannot. Then asking for transparency is the one thing you must not do, because that is what summons the checkerboard -- and a checkerboard is unrecoverable, its squares run under the character's edges. Send the key variant of the prompt instead (
reference/prompts.md, Drawing without transparency): it asks for a flat pure green background and mentions transparency nowhere. Then remove that green:python3 <skill-dir>/scripts/key.py characters/<name>/directions.png --in-placemascot.pydoes this for you when it finds a sheet on a solid key colour, so normally you can just run the build and let it happen.
Hedging does not work. A prompt that asks for transparency and adds "use green if you cannot" gets the checkerboard, same as before. The two prompts are alternatives, never combined.
screen.py names which case you are in:
| what it prints | what happened | what to do |
|---|---|---|
alpha: ok |
real alpha channel | nothing |
alpha: MISSING (solid #02F902 background -- key.py can remove it) |
drawn on the key colour | key it out, or let mascot.py do it |
alpha: MISSING (painted checkerboard, 32px squares -- redraw) |
asked for transparency a tool could not give | redraw with the right prompt for your tool |
alpha: MISSING (opaque background) |
flattened onto white or a scene | redraw; nothing can be keyed out of this |
The last two cannot be repaired. A white background cannot be keyed because the character's own cream and white areas would go with it, which is also why the key colour is a saturated green and never white, grey or black.
Writing the description
This decides whether the result is good, so spend a sentence on it rather than passing the user's word through raw. "fox" gives a worse fox than "a cute chibi fox with warm orange fur, a cream muzzle and dark ear tips". Name the colours and two or three distinguishing features, in one sentence.
Avoid, because each breaks the alignment the effect depends on:
- Long loose hair over the shoulders. Tie it back or put it under a hat. It gets drawn differently in each sheet and the mascot lurches when clicked. Most common failure by far.
- Anything wider than the head -- big wings, wide headdresses. They get clipped at the cell edges.
- Held props. Staffs, mugs, instruments. The framing is head and shoulders only.
Good: a chibi robot with a mint-green boxy head and a single wide visor screen,
a chibi grandmother with silver hair in a neat bun and round gold spectacles.
Drawing in another style
Six looks are available: colour (the default), ink, sketch, riso, paper and pixel.
They change only how the character is drawn -- the framing, proportions, reused body and
margins are shared, so a style cannot break the alignment.
reference/prompts.md has the paragraph for each. Swap it into both prompts for that
character, and name the style again in the EXPRESSIONS prompt so the second sheet does not
drift back to the default. Through the API path it is a flag:
python3 <skill-dir>/scripts/mascot.py fox --style riso --describe "a chibi fox with orange fur"
Keep one style per character across both its sheets. The caveats for ink, sketch and
pixel are at the bottom of reference/prompts.md -- read them before using those three.
From a user's own image
Same loop, but for step 1 pass their image to your image tool with the REFERENCE prompt
from reference/prompts.md. It redraws their character as a directions sheet in the
style the pipeline needs. Step 2 onward is unchanged.
Afterwards
Built atlases land in public/mascots/<name>-{directions,reactions}.webp (or --dest).
The source sheets stay in characters/<name>/ so a character can be rebuilt without
redrawing it; say so, and leave it to the user whether to keep or delete them.
Then Put it on the page.
Making several
One at a time, reporting as you go.
Going deeper
reference/design-rules.md explains why each art rule exists, what each failure looks
like, and how the two sheets are matched. Read it when a character fails in a way the
script's messages do not cover.