Generating one cute portrait is easy. Generating eighteen versions that do not jump, resize, or change clothes is the actual engineering problem.
We already had a useful assistant: it floated, reacted to project cards, moved beside focused form fields, and displayed contextual messages. But its face was a generic yellow SVG. The next goal was personal: make the guide look like the portfolio owner.
For this stage we used Page Mascot’s character system. It uses two 3×3 sprite sheets—nine gaze directions and nine expressions—and swaps cells with CSS background positioning. That gives you cursor attention and click reactions without running a frame-by-frame animation loop.
Why two sprite sheets?
Up-left, up, up-right, left, center, right, down-left, down, down-right. The expression stays calm; only the head direction changes.
Blink, heart, sparkle, surprise, starstruck, bashful, sleepy, dizzy, and delighted. Every frame faces forward.
The component calculates the pointer angle, selects a direction cell, and briefly swaps to reaction cells after a click. The browser only changes background position and opacity; the images do the visual work.
Watch the Custom Avatar Build
See the interactive avatar result and follow the workflow visually before reproducing it in your own project.
Step 1: install the tool and skill
npm install page-mascot
# Optional: install the agent skill that generates and validates characters
npx skills add nilbuild/page-mascot --skill page-mascot --global --yes
You can download an existing character from the gallery, or create a new one from a photo. A custom likeness needs an image-capable agent or an image API.
Step 2: choose a useful portrait
A full-body cutout can work, but the face must still be large and clear enough to inspect. The best reference has visible hair shape, facial hair, skin tone, and upper clothing. Avoid busy backgrounds, sunglasses, dramatic side lighting, and hands covering the face.
Our useful cues were: medium-brown South Asian skin, thick swept-up black quiff, neat moustache and short beard, thick eyebrows, friendly smile, and a navy top. We deliberately simplified the original blazer after it caused inconsistent shoulder shapes.
Step 3: generate the direction sheet
This prompt is designed for an image generator that supports a genuine transparent-background option. Set transparency in the tool itself—prompt text alone cannot guarantee an alpha channel.
Use the attached portrait only as an identity reference. Redraw the person as a polished big-head chibi website mascot. Do not copy the photograph’s realism, lighting, body proportions, or background.
Preserve these identifying details: [skin tone], [hair shape and colour], [facial hair], [glasses if any], [eye colour], and [top colour]. Use clean storybook sticker art, expressive eyes with catchlights, confident dark outlines, warm colours, light cel shading, and a friendly professional expression.
Create one square 3x3 sprite sheet containing exactly nine head directions:
row 1: up-left, up, up-right
row 2: left, straight ahead, right
row 3: down-left, down, down-right
Every cell must use the same character scale and placement. The body must face forward in all nine cells. Draw one compact head-and-shoulders body and reuse that identical body shape in every cell; only the head rotates. Use a simple top with a narrow shoulder silhouette. Keep wide empty margins on all sides. Nothing may touch or cross a cell boundary.
Head and upper shoulders only—no arms, hands, legs, held objects, text, labels, borders, shadows, symbols, or background. Keep the same calm expression in every cell. Square PNG, at least 1024x1024, with real alpha transparency.
The first failure—and the fix
Our first beautiful sheet failed validation. The blazer lapels and shoulder width changed whenever the head turned. On a static grid that is easy to miss. In the browser it makes the character “breathe sideways” every time the cursor crosses a direction boundary.
We tried a stricter prompt, but the blazer still produced too much variation. The successful fix was not another adjective—it was a design change. We replaced the blazer with a simple navy crew-neck top and a compact rounded shoulder capsule. The direction-sheet shoulder spread dropped to a usable range.
Step 4: generate the reaction sheet
Attach the accepted direction sheet—not the original photograph—as the reference for the second generation.
The attached image is the approved 3x3 direction sheet. Create a matching 3x3 reaction sheet for exactly the same character.
Copy the same palette, outline weight, hair shape, facial hair, proportions, head size, clothing, shoulder silhouette, and placement. Every cell faces straight toward the viewer. Only the facial expression changes.
Expressions from left to right, top to bottom:
1. gentle blink
2. blink with one small red heart above
3. blink with three small yellow sparkles above
4. wide surprised eyes and small round mouth
5. star-shaped eyes and happy smile
6. closed eyes with strong cheek blush
7. sleeping eyes with small blue zzz
8. spiral eyes with a wavy dizzy mouth
9. closed eyes with a broad joyful grin
Reuse one identical body in all nine cells. Match the direction sheet’s exact scale and cell position. Preserve wide empty margins. No extra characters, text, labels, borders, shadows, or background. Square PNG, at least 1024x1024, with real alpha transparency.
Step 5: validate before integration
Place the source files at characters/your-name/directions.png and characters/your-name/reactions.png, then let the skill build WebP atlases and measure alignment:
python3 ~/.codex/skills/page-mascot/scripts/mascot.py \
your-name --skip-generate
The validator checks genuine alpha transparency, shoulder consistency, cross-sheet scale, body overlap, palette similarity, width change, and rendered vertical shift. Our final build reported 0.00 rendered pixels of reaction movement, 93% cross-sheet overlap during fitting, and only 0.6% width change.
Look at the middle-left and middle-right cells. Left must face the viewer’s left, and right must face the viewer’s right. Numerical alignment tests cannot detect a mirrored direction order.
Step 6: render it in React
The build writes two optimized files to public/mascots. Integration is intentionally small:
import { Mascot } from 'page-mascot'
export function PortfolioMascot() {
return (
<Mascot
directions="/mascots/your-name-directions.webp"
reactions="/mascots/your-name-reactions.webp"
size={118}
label="Your name, the website guide"
className="portfolio-mascot"
/>
)
}
On fine-pointer devices, the component follows pointer direction. Touch devices avoid unnecessary cursor tracking. Clicking the mascot cycles through friendly reactions and applies a squash animation while respecting prefers-reduced-motion.
Step 7: combine the mascot with contextual UI
Page Mascot owns the face direction and click reactions. Our assistant provider still owns messages, floating position, field docking, priority, minimization, and form events. Keeping those layers separate made the character personal without rewriting the already-working interaction system.
<div
className={`assistant ${dock ? 'is-anchored' : ''}`}
style={dock ? { left: dock.left, top: dock.top } : undefined}
>
<MessageBubble mood={event.mood}>{event.message}</MessageBubble>
<Mascot
directions="/mascots/abhay-directions.webp"
reactions="/mascots/abhay-reactions.webp"
size={dock ? 96 : 118}
label={`Abhay guide, currently ${event.mood}`}
/>
</div>
Master AI prompt: do the entire implementation
Use the page-mascot workflow to create and integrate a custom cursor-tracking mascot from my attached portrait.
First inspect the existing React/Next.js site and identify its current avatar or assistant component. Preserve its messages, form logic, positioning behaviour, event handlers, accessibility, and visual hierarchy.
Create a recognizable big-head chibi character using only durable identity features visible in my photo: hair, facial hair, skin tone, glasses if present, and top colour. Generate two genuine-transparent 3x3 PNG sprite sheets:
- directions: nine head directions with one calm expression
- reactions: nine front-facing expressions (blink, heart, sparkle, surprise, starstruck, blush, sleep, dizzy, delight)
The body must be a simple head-and-shoulders design, identical in every cell. Keep the same scale, x/y placement, margins, clothing, palette, and outline weight across both sheets. Do not use arms, props, long loose hair, wide accessories, text, labels, borders, shadows, or backgrounds.
Run the page-mascot build and verification pipeline. Reject sheets with missing alpha, painted checkerboards, inconsistent shoulders, wrong direction order, visible cross-sheet movement, or large palette drift. If clothing causes body variation, simplify it before regenerating.
Install page-mascot using the project’s package manager. Integrate the generated WebP atlases into the existing assistant instead of creating a second floating widget. The mascot must track cursor direction on desktop, react on click, remain keyboard accessible, respect prefers-reduced-motion, and retain the site’s mobile behaviour.
Verify the final character visually at rest, while tracking left/right, after clicking, and beside a focused form field. Run type checking, lint, tests, and production build. Report the generated assets, changed files, verification metrics, and test results.
Common failures you should expect
Enable transparent background in the image tool. If unavailable, generate on a pure chroma-key green or magenta background and remove it. Do not ask an opaque-only model to “draw transparency.”
The direction and reaction sheets disagree on body position, scale, or clothing. Regenerate the failing sheet using the accepted sheet as the visual reference.
Simplify the outfit. Complex jackets, lapels, long hair, held objects, and wide accessories are alignment traps.
Large source-sheet margins are necessary, so tune the rendered component size and wrapper carefully instead of cropping the source cells differently.
Reference and credit
Explore the live character gallery and documentation at koboyo.com/page-mascot. The underlying open-source package is available at nilbuild/page-mascot on GitHub.
A personal mascot is half illustration, half test suite.
The art creates recognition. Alignment validation makes the illusion believable.
Read Part 1: Interaction architecture →