Use when creating or changing VS Code chat pet sprite art, sprite sheets, state animations, eye treatments, Stable/Insiders variants, or pet transitions under src/vs/workbench/contrib/chat/browser/widget/media/chatPet.
70
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Create pet sprites that feel like one continuous character rather than separate drawings. Start from the current pet body, preserve its visual anchor, and make every difference intentional.
src/vs/workbench/contrib/chat/browser/widget/media/chatPet/src/vs/workbench/contrib/chat/browser/widget/chatPetWidget.tssrc/vs/workbench/contrib/chat/browser/widget/media/chatPet.csssrc/vs/workbench/contrib/chat/test/browser/widget/chatPetWidget.test.tsRead the current implementation before drawing. Existing dimensions and timings may have changed since this skill was written.
This skill distills the complete pet PR history. When rules conflict, use this precedence:
In particular, animation cleanup PR #330399 supersedes the old revive-sign flow and several earlier sprite/timing assumptions.
| PR | Durable learning |
|---|---|
| #327063 | The pet is one semantic button; sprite canvases, images, eyes, and effects are visual-only and aria-hidden. Resolve asset URLs through FileAccess. |
| #327412 | Model reactions as explicit states. Do not immediately repeat ordinary random reactions; sample rare transformations separately. |
| #327588 | Double-buffer sprite images and swap only after the pending image loads. Never blank the active sprite while a new source is loading. |
| #327696 | Stable/Insiders is a typed, persisted appearance choice. Keep variant naming and geometry systematic. |
| #327714 | Animated art is a horizontal sprite sheet plus a deliberate static reduced-motion PNG. Timing is runtime data, not image metadata. |
| #328334 | Persistent behavior such as “on the run” belongs in service state. Multi-phase animations advance from completion signals, not guessed delays. |
| #328480 | A drag release must not also trigger a click reaction. Gestures that share pointer events need explicit suppression/ownership. |
| #328530 | Waking consumes the interaction that woke the pet; it must not also trigger a random reaction. Reduced motion may skip the wake transition. |
| #329121 | Coalesce gaze updates, pause sprite timers while the document is hidden, and resume from elapsed time rather than replaying missed frames. |
| #329347 | Keep pixel-art pose changes in sprite frames and spatial motion in layout/CSS/physics. Complete falls from transition signals. |
| #329729 | Face the interaction before starting a reaction. Give each transient state an explicit lifetime and use real completion events for chained phases. |
| #329852 | Pointer and keyboard gestures need equivalent outcomes. Reduced motion preserves the state change while removing the travel animation. |
| #329867 | Attention animations are bounded; clapping stops after the confirmation-attention window even if the confirmation remains pending. |
| #330160 | Eyes are a composited runtime layer with tracking/blinking modes. Rapid facing changes can trigger a separate dizzy state. Wide art must respect live bounds. |
| #330275 | Physics uses the body footprint, bounded frame steps, and explicit impact moments. Recompute geometry when layout changes during flight. |
| #330399 | Latest animation contract: wide sleep/wake art, frame-aware blink composition, fixed-orientation decorations, and reverse despawn → forward respawn using one effect sheet. |
The canonical art unit is one logical pixel:
8×8 source pixels4×4 CSS pixels at the standard 96×96 source → 48×48 display scaleCore silhouettes, facial features, props, and repeated details must use the same logical-pixel scale. Do not make a new object from smaller or larger blocks simply to fit more detail.
Effects may deliberately subdivide the logical pixel—for example, small stars, bubbles, or curved motion—but the subdivision must:
Use nearest-neighbor rendering only. Keep transparent backgrounds and hard pixel edges. Do not introduce blur, feathered alpha, color interpolation, or accidental off-grid scaling.
Never redraw the pet body from memory.
This preserves:
The first and last key poses must transition cleanly to idle or rendering without a body-position, silhouette, baseline, or scale pop.
The default source canvas is 96×96, or 12×12 logical pixels.
Use a larger canvas only when content intentionally extends outside the canonical body box:
Do not center the body inside a wider frame. Keep the body in the canonical 96×96 region and let the prop/effect overhang. Declare every nonstandard source dimension in chatPetWidget.ts and update wide-sprite boundary handling.
The runtime collision and movement box belongs to the pet body, not to a decorative overhang.
The standard open eye is 1×2 logical pixels:
8×16 source pixels4×8 CSS pixelsUse a different eye shape only when the state communicates a special expression, such as sleep, dizzy, love, worry, or impact.
The DOM eye layer has two independent modes:
States that use runtime gaze must use eye-less *-tracking-96 assets. States that use DOM blinking, such as typing, button press, or love in PR #330399, must also leave the corresponding pupil area available for the overlay.
When adding or changing a DOM-eye state:
doesChatPetStateTrackCursor when appropriate.doesChatPetStateBlink(state, frameIndex) when appropriate.Never combine baked pupils with the runtime eye overlay.
States with a fixed expression should bake the eyes into the art and opt out of cursor tracking. Preserve the standard eye position unless the expression intentionally changes it.
Stable and Insiders are palette variants of the same animation.
They must have identical:
Create one geometry master and apply the two established palettes. Do not redraw variants independently.
Review the two variants side by side. Any shape difference is a bug unless the product explicitly requires it.
Author one canonical facing direction and let the runtime mirror it for the other direction.
Test both directions because mirroring also changes where wide sprites overhang. Keep enough room for boundary correction near the left and right edges.
Decorations that must retain their screen orientation—such as text, musical notation, or directional symbols—must not be baked into a layer that the runtime mirrors. Model them as fixed-orientation decorations or render them separately.
Each animated state needs both variants of:
buddy-<state>-<variant>-<height>.pngbuddy-<state>-<variant>-<height>.spritesheet.pngThe sprite sheet contract is:
sheet width = frame width × frame count
sheet height = frame heightEvery frame uses the same rectangle. Never shift frame boundaries or add per-frame padding.
Choose a meaningful static pose that communicates the state without motion. Do not assume the first animation frame is automatically the best reduced-motion fallback.
Animate key poses, not noise.
Frame duration is part of the art. Define it in the matching *_FRAME_DURATIONS array in chatPetWidget.ts; do not encode or infer timing from the PNG.
Keep the transient-state duration consistent with the intended loops or one-shot playback. Explicitly choose whether the sheet:
If one sheet is reused in reverse, as with respawn → despawn:
PR #330399 is the reference pattern: the respawn burst plays backward to despawn at the bottom, then forward to respawn at the top. The old revive-sign interstitial is superseded.
Use sprite frames for changes to the pet or prop silhouette. Use CSS transforms or the physics loop for:
Do not redraw every translated/rotated pose into the sheet. Conversely, do not use a CSS transform to fake a body deformation that should be readable in the pixel art.
Advance multi-phase behavior from deterministic completion signals:
animationend for CSS keyframestransitionend/transitioncancel for fallsDo not guess a completion time with an unrelated timeout.
Sprite timing uses elapsed-time scheduling at frame boundaries, not a display-refresh polling loop. A late callback recalculates the correct frame and shortens the next delay.
Review the sprite in the same environment where it runs:
Pixels intended to pass behind the platform must be clipped or layered behind it. Do not leave body/effect pixels visible below an occluding platform.
Movement should preserve the body anchor until physics intentionally moves the whole pet. Decorative frame size must not change the physics target.
The rendered state has a deliberate priority. Busy/task states must not be accidentally hidden by decorative reactions. Preserve the current order in getChatPetBaseState and the transient-state guards in getChatPetRenderedState.
Interaction rules:
Rare interactions should be sampled independently from the ordinary non-repeating pool so adding a common reaction does not silently change an easter egg's probability.
Keep the current rendering architecture:
getAttribute('src'); .src may normalize VS Code resource URLs.imageSmoothingEnabled = false and image-rendering: pixelated.requestAnimationFrame for physics, but cap integration steps to prevent tunneling after a slow frame.When adding a state, update every applicable surface:
ChatPetState.getChatPetSpriteName.getChatPetFrameDurations.getSpriteSources.Do not add art without completing the runtime contract.
Keep the visuals silent: images, canvases, eyes, and effects use empty alt text and aria-hidden. The button owns the accessible label, focusability, and localized status() announcements. Hidden, dead, or non-interactive states must leave the tab order.
8×8 source-pixel logical unit.1×2 logical pixels.aria-hidden; the button owns semantics and tab order.ChatPetWidget tests cover state names, exact timings, geometry, state priority, and reduced motion.Run the focused unit tests using the repository's unit-tests skill. At minimum, run the ChatPetWidget test suite.
Prefer exporting pure helpers for timing, geometry, random selection, and state precedence, then test them directly. Use fake timers for scheduler behavior. Assert exact frame-duration arrays so art and runtime timing cannot drift independently.
The public showcase lives in the sibling vscode-chat-pet repository. Use its update-pet-previews skill after production sprites are final.
When replacing an existing APNG, prefer a new versioned filename in the README so GitHub and browser caches cannot keep showing the previous animation.
b0258bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.