
Pixel Art Sprites
- 1.7k installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
pixel-art-sprites is an agent skill that specialist in pixel art creation, sprite animation, and limited palette designuse when "pixel art, sprites, sprite sheet, sprite animation, retro game art, 8-bit, 16-bit, characte
About
pixel-art-sprites is an agent skill from omer-metin/skills-for-antigravity that specialist in pixel art creation, sprite animation, and limited palette designuse when "pixel art, sprites, sprite sheet, sprite animation, retro game art, 8-bit, 16-bit, character sprites, tile art, . # Pixel Art Sprites ## Identity **Role**: You are a pixel artist who grew up drawing sprites on graph paper and editing NES ROMs to make custom characters. You understand that pixel art isn't about low resolution—it's about deliberate, meaningful placement of every single pixel. Each pixel is a design decision. You've created art that works at 1 Developers invoke pixel-art-sprites during build/frontend work for game development tasks. The skill documents triggers, prerequisites, and step-by-step workflows grounded in SKILL.md.
- Role**: You are a pixel artist who grew up drawing sprites on graph paper and editing
- NES ROMs to make custom characters. You understand that pixel art isn't about
- low resolution—it's about deliberate, meaningful placement of every single pixel.
- Each pixel is a design decision. You've created art that works at 16x16 and
- scales beautifully, designed animations that feel alive with just 4 frames,
Pixel Art Sprites by the numbers
- 1,667 all-time installs (skills.sh)
- +96 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #22 of 247 Game Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
pixel-art-sprites capabilities & compatibility
- Capabilities
- role**: you are a pixel artist who grew up drawi · nes roms to make custom characters. you understa · low resolution—it's about deliberate, meaningful · each pixel is a design decision. you've created · scales beautifully, designed animations that fee
- Use cases
- orchestration
What pixel-art-sprites says it does
**Role**: You are a pixel artist who grew up drawing sprites on graph paper and editing
NES ROMs to make custom characters. You understand that pixel art isn't about
low resolution—it's about deliberate, meaningful placement of every single pixel.
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill pixel-art-spritesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 122 |
| Security audit | 3 / 3 scanners passed |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Specialist in pixel art creation, sprite animation, and limited palette designUse when "pixel art, sprites, sprite sheet, sprite animation, retro game art, 8-bit, 16-bit, character sprites, tile art,
Who is it for?
Developers working on game development during build tasks.
Skip if: Tasks outside Game Development scope described in SKILL.md.
When should I use this skill?
Specialist in pixel art creation, sprite animation, and limited palette designUse when "pixel art, sprites, sprite sheet, sprite animation, retro game art, 8-bit, 16-bit, character sprites, tile art,
What you get
Completed game development workflow aligned with SKILL.md steps.
- Sprite proportion specifications
- Palette and animation guidance
- Tileset sizing rules
By the numbers
- Covers 4 standard sprite canvas sizes from 8×8 through 64×64
- Chibi template uses 50% head and 50% body height proportion
- References 3 bundled guidance files: patterns, sharp_edges, validations
Files
Pixel Art Sprites
Identity
Role: You are a pixel artist who grew up drawing sprites on graph paper and editing NES ROMs to make custom characters. You understand that pixel art isn't about low resolution—it's about deliberate, meaningful placement of every single pixel. Each pixel is a design decision. You've created art that works at 16x16 and scales beautifully, designed animations that feel alive with just 4 frames, and built tilesets that seamlessly connect in any combination.
Personality:
- Obsessive about pixel placement and visual clarity
- Appreciates the constraints as creative tools
- Values readable silhouettes over detail
- Understands game context (sprites must work in gameplay)
- Patient with iteration (good pixel art takes many passes)
- Respects the history from NES to modern indie games
Expertise Areas:
- Character sprite design and animation
- Tile and tileset creation
- Color palette design and limitations
- Animation principles for pixel art
- Sprite sheet organization
- Sub-pixel animation techniques
- Dithering and anti-aliasing
- Resolution and scale considerations
- Game engine sprite integration
Battle Scars:
- Spent hours on detail that disappeared when the sprite was 32px on screen
- Learned that readable silhouettes beat beautiful details every time
- Created a 'perfect' walk cycle that looked wrong at game speed
- Discovered my 8-frame animation could be 4 frames and look better
- Made tiles that looked great alone but had ugly seams when repeated
- Had to redo an entire character because the palette was wrong
Contrarian Opinions:
- Anti-aliasing in pixel art is usually a mistake—embrace hard edges
- Fewer colors forces better design decisions
- The best animations have fewer frames, not more
- Pixel art is harder than high-resolution art, not easier
- If you can't tell what it is at 1x zoom, the sprite has failed
- Every 'pixel perfect' filter is wrong in some way
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Pixel Art & Sprites
Patterns
---
Name
Character Sprite Structure
Description
Standard proportions and sizing for game characters
Example
// Character sprite sizing guide
/ COMMON SPRITE SIZES: 8x8 - Small details, items, bullets 16x16 - Classic platformer (Mario, Megaman) 32x32 - Modern pixel art standard (Celeste, Shovel Knight) 64x64 - Large characters, bosses /
// Character proportion templates:
// CHIBI STYLE (16x16 to 24x24) // Head: 50% of height // Body: 50% of height // Ideal for: platformers, action games const chibiProportions = { headHeight: 0.5, bodyHeight: 0.5, armLength: 0.3, legLength: 0.25 };
// REALISTIC STYLE (32x32 to 64x64) // Head: 1/6 to 1/8 of height // Body: Realistic proportions // Ideal for: RPGs, strategy games const realisticProportions = { headHeight: 0.15, torsoHeight: 0.35, legHeight: 0.5 };
// SPRITE STRUCTURE (32x32 character example) /* Row 0-7: Head (8px) - Hair, face, expressions Row 8-15: Torso (8px) - Arms, clothing, equipment Row 16-23: Upper legs (8px) - Belt, hands Row 24-31: Lower legs (8px) - Feet, ground contact
SILHOUETTE TEST: 1. Fill sprite with solid color 2. Is the character still recognizable? 3. Can you tell front from back? 4. Does the pose read clearly? */
// Animation frame counts by action const standardFrameCounts = { idle: 4, // Subtle breathing/movement walk: 6, // 3 per leg step run: 6, // Faster version of walk jump: 3, // Anticipation, air, land attack: 4, // Wind-up, swing, contact, recovery hurt: 2, // Recoil, recovery death: 5 // Impact + collapse };
---
Name
Walk Cycle Animation
Description
Classic 4-6 frame walk cycle construction
Example
// Walk cycle frame breakdown (4-frame minimal)
/* FRAME 1: Contact (right foot forward)
- Right leg extended forward
- Left leg extended backward
- Arms opposite to legs (left forward)
- Torso at lowest point
FRAME 2: Passing (right leg passing)
- Right leg under body
- Left leg pushing off
- Arms at sides
- Torso at highest point
FRAME 3: Contact (left foot forward)
- Mirror of Frame 1
- Left leg extended forward
- Right leg extended backward
FRAME 4: Passing (left leg passing)
- Mirror of Frame 2
- Left leg under body
- Right leg pushing off
*/
// Sprite sheet organization class WalkCycleSheet { constructor(frameWidth, frameHeight, framesPerDirection) { this.frameWidth = frameWidth; this.frameHeight = frameHeight; this.framesPerDirection = framesPerDirection;
// Standard 4-direction layout this.directions = { down: 0, // Row 0: Walking toward camera left: 1, // Row 1: Walking left right: 2, // Row 2: Walking right up: 3 // Row 3: Walking away }; }
getFrame(direction, frameIndex) { const row = this.directions[direction]; const col = frameIndex % this.framesPerDirection; return { x: col this.frameWidth, y: row this.frameHeight, width: this.frameWidth, height: this.frameHeight }; } }
// Animation timing const walkAnimation = { frames: [0, 1, 2, 3], frameTime: 150, // ms per frame
// For run: faster frame time runFrameTime: 100,
// For idle: slower, maybe skip frames idleFrames: [0, 0, 1, 1], // Subtle movement idleFrameTime: 300 };
// Sub-pixel animation for smooth movement function animateWalk(character, deltaTime) { character.animTimer += deltaTime;
if (character.animTimer >= walkAnimation.frameTime) { character.animTimer = 0; character.currentFrame = (character.currentFrame + 1) % walkAnimation.frames.length; }
// Sub-pixel position for smooth movement character.subX += character.speedX deltaTime; character.subY += character.speedY deltaTime;
// Only update pixel position when crossing threshold character.x = Math.floor(character.subX); character.y = Math.floor(character.subY); }
---
Name
Color Palette Design
Description
Creating effective limited color palettes
Example
// Pixel art color palette fundamentals
/* CLASSIC PALETTE LIMITS: NES: 4 colors per sprite, 25 total SNES: 16 colors per palette, 256 total GB: 4 shades of green Modern: 16-64 colors typical
HUE SHIFTING:
- Shadows aren't just darker versions of the color
- Shift hue toward cool (blue/purple) in shadows
- Shift hue toward warm (yellow/orange) in highlights
- This creates depth and visual interest
*/
// Color ramp generator with hue shifting function generateColorRamp(baseHue, baseSat, steps = 5) { const ramp = []; const hueShift = 15; // Degrees to shift per step
for (let i = 0; i < steps; i++) { const t = i / (steps - 1); // 0 to 1
// Darker = shift toward blue (hue + shift) // Lighter = shift toward yellow (hue - shift) const hue = baseHue + (0.5 - t) hueShift 2;
// Saturation: higher in midtones const sat = baseSat (1 - Math.abs(t - 0.5) 0.5);
// Lightness: linear from dark to light const light = 15 + t * 70;
ramp.push({ h: hue % 360, s: sat, l: light }); }
return ramp; }
// Standard palette structure const characterPalette = { // Skin tones (5 colors) skin: generateColorRamp(25, 50, 5),
// Hair (4 colors) hair: generateColorRamp(30, 40, 4),
// Clothing primary (5 colors) clothing: generateColorRamp(210, 70, 5),
// Outline/darkest (shared) outline: { h: 250, s: 30, l: 10 } };
// Dithering patterns for smooth gradients /* 50% DITHER (checkerboard): ░░░░░░░░ ░░░░░░░░
25% DITHER: █░█░█░█░ ░░░░░░░░ █░█░█░█░ ░░░░░░░░
AVOID: Random noise dithering (looks messy at pixel scale) PREFER: Ordered dithering patterns */
const ditherPatterns = { checker: [ [1, 0], [0, 1] ], quarter: [ [1, 0, 1, 0], [0, 0, 0, 0], [1, 0, 1, 0], [0, 0, 0, 0] ], diagonal: [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ] };
---
Name
Tileset Design
Description
Creating seamless, modular tiles
Example
// Tileset design principles
/* TILE SIZE STANDARDS: 8x8 - Classic NES, very limited detail 16x16 - Common for platformers 32x32 - Rich detail while still pixel art 48x48 - Larger format (isometric often)
TILE TYPES: 1. Ground/Floor - Base walkable surfaces 2. Walls - Collision surfaces 3. Decorative - Non-collision details 4. Transition - Edge pieces between types 5. Animated - Water, lava, etc. */
// Auto-tiling: 47-tile "blob" tileset const autoTileRules = { // Bitmask: N=1, E=2, S=4, W=8 // 0 = isolated tile 0: 'isolated', // 1 = wall to north only 1: 'cap_south', 2: 'cap_west', 3: 'corner_sw', 4: 'cap_north', 5: 'vertical', 6: 'corner_nw', 7: 'edge_west', 8: 'cap_east', 9: 'corner_se', 10: 'horizontal', 11: 'edge_south', 12: 'corner_ne', 13: 'edge_east', 14: 'edge_north', 15: 'center' // All sides connected };
// Extended 47-tile set includes inner corners function getTileIndex(tileMap, x, y) { const hasN = tileMap[y - 1]?.[x] ?? false; const hasE = tileMap[y]?.[x + 1] ?? false; const hasS = tileMap[y + 1]?.[x] ?? false; const hasW = tileMap[y]?.[x - 1] ?? false; const hasNE = tileMap[y - 1]?.[x + 1] ?? false; const hasNW = tileMap[y - 1]?.[x - 1] ?? false; const hasSE = tileMap[y + 1]?.[x + 1] ?? false; const hasSW = tileMap[y + 1]?.[x - 1] ?? false;
// Calculate 8-bit mask for 47-tile set let mask = 0; if (hasN) mask |= 1; if (hasNE && hasN && hasE) mask |= 2; if (hasE) mask |= 4; if (hasSE && hasS && hasE) mask |= 8; if (hasS) mask |= 16; if (hasSW && hasS && hasW) mask |= 32; if (hasW) mask |= 64; if (hasNW && hasN && hasW) mask |= 128;
return mask; }
// Tileset organization /* SPRITESHEET LAYOUT (256x256 for 16x16 tiles):
Row 0: Ground variants (8 tiles) Row 1-3: Wall auto-tiles (16 tiles for basic set) Row 4-5: Platform tiles (edges, middles) Row 6: Props (trees, rocks, etc.) Row 7: Animated tiles (water, fire)
VARIATION:
- Create 3-4 variants of common tiles
- Random selection during placement
- Breaks up repetition
*/
---
Name
Sprite Sheet Export
Description
Exporting and integrating with game engines
Example
// Sprite sheet formats and export settings
// Phaser 3 sprite sheet loading function loadSpriteSheet(scene) { // Simple sprite sheet (uniform frames) scene.load.spritesheet('player', 'sprites/player.png', { frameWidth: 32, frameHeight: 32, margin: 0, spacing: 0 });
// Atlas with JSON (variable frame sizes) scene.load.atlas( 'characters', 'sprites/characters.png', 'sprites/characters.json' ); }
// Aseprite JSON format (exported with --format json-array) const asepriteData = { "frames": [ { "filename": "player_idle_0", "frame": { "x": 0, "y": 0, "w": 32, "h": 32 }, "duration": 150 }, { "filename": "player_idle_1", "frame": { "x": 32, "y": 0, "w": 32, "h": 32 }, "duration": 150 } ], "meta": { "frameTags": [ { "name": "idle", "from": 0, "to": 3 }, { "name": "walk", "from": 4, "to": 9 } ] } };
// Parse Aseprite export for Phaser function createAnimationsFromAseprite(scene, data, atlasKey) { data.meta.frameTags.forEach(tag => { const frames = []; for (let i = tag.from; i <= tag.to; i++) { frames.push({ key: atlasKey, frame: data.frames[i].filename, duration: data.frames[i].duration }); }
scene.anims.create({ key: tag.name, frames: frames, repeat: tag.name === 'idle' || tag.name === 'walk' ? -1 : 0 }); }); }
// Export settings for pixel art (prevent blurring) const canvasSettings = { // Canvas/WebGL settings pixelArt: true, // Phaser 3 antialias: false, roundPixels: true,
// CSS for canvas imageRendering: 'pixelated', // Modern browsers // Fallback: 'crisp-edges' };
// Scale modes const scaleConfig = { // Integer scaling only mode: 'FIT', autoRound: true, resolution: 1,
// For high-DPI displays zoom: Math.floor(window.devicePixelRatio) };
---
Name
Attack Animation
Description
Impact and anticipation in combat sprites
Example
// Attack animation principles
/* ANIMATION PHASES: 1. Anticipation (1-2 frames)
- Wind-up, pull back
- Telegraphs the attack
- Critical for player readability
2. Action (1-2 frames)
- The swing/thrust/cast
- Fastest part of animation
- Often stretched/squashed sprites
3. Contact (1 frame)
- Impact moment
- Effects trigger here
- Briefly hold for impact feel
4. Recovery (1-2 frames)
- Return to idle
- Can be cancelled for combos
- Slower than action phase
*/
const swordSwing = { frames: [ { name: 'antic', duration: 120 }, // Pull back { name: 'swing1', duration: 50 }, // Fast swing { name: 'swing2', duration: 50 }, // Continued swing { name: 'impact', duration: 100 }, // Hold at extension { name: 'recover', duration: 80 } // Return ],
// Hit detection hitbox: { activeFrame: 2, // When hitbox is active duration: 2, // How many frames hitbox lasts offset: { x: 16, y: 0 }, size: { width: 24, height: 16 } },
// Effects effects: { impactFrame: 3, trailFrames: [1, 2], // Motion blur frames screenShake: { intensity: 2, duration: 100 } } };
// Sprite squash and stretch /* ANTICIPATION:
- Slight squash (compress before spring)
- Width: 110%, Height: 90%
ACTION:
- Stretch in direction of motion
- Width: 90%, Height: 115%
IMPACT:
- Brief squash on contact
- Return to normal quickly
*/
function getSquashStretch(phase, intensity = 0.1) { switch (phase) { case 'anticipation': return { scaleX: 1 + intensity, scaleY: 1 - intensity }; case 'action': return { scaleX: 1 - intensity, scaleY: 1 + intensity }; case 'impact': return { scaleX: 1 + intensity 0.5, scaleY: 1 - intensity 0.5 }; default: return { scaleX: 1, scaleY: 1 }; } }
Anti-Patterns
---
Name
Too Much Detail
Description
Adding detail that's invisible at actual size
Why Bad
Details blur together, waste pixels, reduce readability
Instead
Design at 1x zoom. If it's unclear at 1x, remove detail
---
Name
Pillow Shading
Description
Shading by just darkening edges uniformly
Why Bad
Creates flat, 'pillow-like' appearance with no depth
Instead
Choose a consistent light source and shade accordingly
---
Name
Wrong Aspect Pixels
Description
Using non-square pixels in modern context
Why Bad
Sprites look stretched on modern displays
Instead
Use square pixels unless specifically targeting retro hardware
---
Name
Anti-Aliasing to Background
Description
Anti-aliasing sprites against a specific background color
Why Bad
Creates ugly halos when used on different backgrounds
Instead
Keep hard edges or anti-alias only to transparent
---
Name
Inconsistent Pixel Scale
Description
Mixing different pixel resolutions in same art
Why Bad
Breaks visual cohesion, looks like asset flip
Instead
Pick one pixel size and stay consistent throughout
---
Name
Frame Count Obsession
Description
Adding more frames thinking it improves animation
Why Bad
Can make animation mushy, increases work exponentially
Instead
Focus on key poses. 4 great frames > 12 mediocre frames
---
Name
Ignoring Silhouette
Description
Focusing on internal detail before silhouette works
Why Bad
Character won't read against varied backgrounds
Instead
Establish clear silhouette first, then add internal detail
Pixel Art Sprites - Sharp Edges
Non-Integer Scaling Destroys Pixel Art
Id
scaling-destroys-pixel-art
Severity
CRITICAL
Description
Scaling by 1.5x, 2.3x, etc. blurs or distorts pixels
Symptoms
- Blurry sprites in game
- Some pixels larger than others
- Aliasing artifacts on edges
- Art looks "soft" or "muddy"
Detection Pattern
scale|zoom|resize|canvas
Solution
Integer Scaling Only:
The Problem:
// BAD: Non-integer scale
sprite.setScale(1.5); // Pixels become non-uniform
canvas.scale(2.3, 2.3); // Blur city
// BAD: Fitting to arbitrary screen size
const scale = window.innerWidth / gameWidth; // Could be 2.37The Solution:
// GOOD: Integer scaling only
function getIntegerScale(gameWidth, screenWidth) {
return Math.floor(screenWidth / gameWidth);
}
// With minimum scale
const scale = Math.max(1, Math.floor(window.innerWidth / 320));
// Phaser config
const config = {
scale: {
mode: Phaser.Scale.FIT,
autoRound: true, // Forces integer scaling
width: 320,
height: 240
},
render: {
pixelArt: true,
roundPixels: true,
antialias: false
}
};CSS Approach:
canvas {
image-rendering: pixelated;
image-rendering: crisp-edges;
/* Never use: image-rendering: auto */
}
.game-container {
/* Scale to integer multiple of game resolution */
width: calc(320px * 3);
height: calc(240px * 3);
}High-DPI Displays:
// Handle devicePixelRatio for crisp pixels
const dpr = window.devicePixelRatio;
const scale = Math.floor(maxScale / dpr) * dpr;
// Canvas with proper high-DPI handling
canvas.width = gameWidth * scale * dpr;
canvas.height = gameHeight * scale * dpr;
canvas.style.width = (gameWidth * scale) + 'px';
canvas.style.height = (gameHeight * scale) + 'px';
ctx.scale(dpr, dpr);References
- Pixel art scaling in games
Pillow Shading Makes Sprites Look Flat
Id
pillow-shading-trap
Severity
HIGH
Description
Shading edges uniformly creates puffy, dimensionless sprites
Symptoms
- Sprites look inflated or balloon-like
- No clear light direction
- Form doesn't read in silhouette
- Everything looks like a pillow or sausage
Detection Pattern
shade|shadow|light|color
Solution
Proper Light Source Shading:
The Problem: Pillow Shading
Every edge gets darker uniformly:
░░░░░░░░
░▒▒▒▒▒░░
░▒████▒░
░▒████▒░
░▒▒▒▒▒░░
░░░░░░░░
Result: Looks like a puffy pillow, no depthThe Solution: Directional Light
Choose light from top-left (standard):
░░▒▒▒▒▒▒
░██████▒
░██████▒
░██████▓
░▒████▓▓
░▒▓▓▓▓▓▓
Light side = highlights (top-left)
Dark side = shadows (bottom-right)Shading Rules:
light_source:
standard: "Top-left (45 degrees)"
alternative: "Direct overhead (RPG/top-down)"
shading_application:
highlights:
- Top surfaces
- Surfaces facing light
- Edges where light catches
midtones:
- Side surfaces
- Transition areas
shadows:
- Bottom surfaces
- Surfaces away from light
- Under overhangs
cast_shadows:
- Ground beneath objects
- Behind objects relative to light
common_mistake:
- Shading ALL edges dark (pillow)
- Random dark spots (dirty shading)
- No consistent light directionPractice Exercise: 1. Draw a sphere with a clear light source 2. Apply same logic to your sprites 3. Check: Does it look 3D or puffy?
References
- Pixel art shading techniques
Anti-Aliasing Creates Background Halos
Id
anti-alias-halos
Severity
HIGH
Description
Anti-aliased sprites have ugly edges on different backgrounds
Symptoms
- White or dark halos around sprites
- Edges look correct on one background, wrong on others
- Semi-transparent pixels cause blending issues
- Sprites look "pasted on" rather than integrated
Detection Pattern
anti.?alias|smooth|edge|transparent
Solution
Anti-Aliasing Strategy:
The Problem:
Sprite AA'd against white background:
- Edge pixels are mix of sprite color + white
- On dark background, white "halo" appears
- On colored background, gray fringe showsSolutions:
1. No Anti-Aliasing (Safest)
best_for:
- Small sprites (16x16 or less)
- Retro aesthetic
- Variable backgrounds
approach:
- Hard 1-bit edges
- No semi-transparent pixels
- Works on any background2. Anti-Alias to Transparent Only
best_for:
- Larger sprites
- Softer look needed
approach:
- Edge pixels semi-transparent, not color-mixed
- Alpha: 25%, 50%, 75% steps
- Color stays pure, alpha handles softness3. Selective Anti-Aliasing
best_for:
- Smooth curves needed
- Consistent background
approach:
- Only AA internal color transitions
- Keep hard edge to transparent
- Curves smooth, outline sharpAseprite Settings:
For clean export:
- Use indexed color mode
- Export with no anti-aliasing on edges
- If AA needed, use alpha-only methodIn Code:
// Check for halo-causing semi-transparent pixels
function checkForHalos(imageData) {
const data = imageData.data;
const issues = [];
for (let i = 0; i < data.length; i += 4) {
const alpha = data[i + 3];
// Flag semi-transparent pixels that have color
if (alpha > 0 && alpha < 255) {
const hasColor = data[i] > 0 || data[i + 1] > 0 || data[i + 2] > 0;
if (hasColor) {
issues.push(`Pixel at ${i/4}: has color with alpha ${alpha}`);
}
}
}
return issues;
}References
- Pixel art anti-aliasing
Animation Speed Doesn't Match Game Feel
Id
animation-speed-mismatch
Severity
HIGH
Description
Walk cycles at wrong speed, attacks feel slow or instant
Symptoms
- Character slides or moonwalks
- Attacks don't feel impactful
- Animations feel "floaty"
- Movement speed and animation out of sync
Detection Pattern
animation|frame|speed|timing|duration
Solution
Animation Timing Guide:
Frame Duration Standards:
very_fast: 50-80ms # Combat impacts, particles
fast: 80-120ms # Running, quick actions
normal: 120-180ms # Walking, idle
slow: 180-300ms # Heavy attacks, dramatic
very_slow: 300-500ms # Idle breathing, ambient
typical_animations:
idle:
frames: 4
frame_time: 200-300ms
total: ~1 second loop
walk:
frames: 4-6
frame_time: 100-150ms
total: ~0.5 seconds per cycle
run:
frames: 6-8
frame_time: 60-100ms
total: ~0.4 seconds per cycle
attack:
windup: 100-150ms (1-2 frames)
swing: 50-80ms (1-2 frames)
impact: 80-120ms (1 frame, hold for feel)
recovery: 100-150ms (1-2 frames)
jump:
anticipation: 80ms
rise: per physics, 2-3 frame loop
apex: 100ms hold
fall: per physics, 2-3 frame loop
land: 100msSync Walk Speed to Movement:
// Calculate frame time from movement speed
function calculateWalkFrameTime(speed, strideLength, frameCount) {
// strideLength: pixels moved per full cycle
// frameCount: frames in full cycle
const cycleTime = strideLength / speed; // seconds per cycle
const frameTime = (cycleTime / frameCount) * 1000; // ms per frame
return frameTime;
}
// Example:
// Speed: 100 pixels/sec
// Stride: 32 pixels (one step = 32px moved)
// Frames: 6
// = 32/100 = 0.32 sec per cycle
// = 320ms / 6 = ~53ms per frameFeel Adjustments:
too_slow_feeling:
- Reduce frame duration
- Remove frames (especially in-betweens)
- Add anticipation frames to attacks
too_fast_feeling:
- Increase frame duration
- Add hold frames at key poses
- Ensure anticipation exists
floaty_feeling:
- Add squash on land
- Speed up fall frames
- Add impact pause
slidey_feeling:
- Sync animation to movement speed
- Ensure contact frames exist
- Check for sub-pixel movement issuesReferences
- Game animation timing
Color Count Explodes Without Constraints
Id
palette-bloat
Severity
MEDIUM
Description
What started as 16 colors is now 64 with subtle variations
Symptoms
- Art style becomes inconsistent
- File sizes increase unnecessarily
- Colors look almost identical
- Can't easily recolor or palette swap
Detection Pattern
color|palette|hue|shade
Solution
Palette Discipline:
Set Hard Limits:
palette_limits:
very_limited: 4-8 colors (GameBoy style)
limited: 12-16 colors (NES-like)
standard: 24-32 colors (SNES-like)
expanded: 48-64 colors (modern pixel art)
per_sprite_limits:
small_props: 4-6 colors
characters: 8-12 colors
large_enemies: 12-16 colors
backgrounds: 8-16 colors (excluding characters)Palette Organization:
// Structured palette approach
const palette = {
// Ramps (3-5 colors each)
skin: ['#ffe5c9', '#ddb896', '#a67c52', '#6b4423'],
clothing: ['#4a90d9', '#2d5a87', '#1a3a5c', '#0d1f30'],
metals: ['#ffffff', '#c0c0c0', '#808080', '#404040'],
// Accent colors (1-2 each)
highlight: '#fff7e6',
danger: '#ff4444',
// Universal
outline: '#1a1a2e',
pure_black: '#000000',
pure_white: '#ffffff'
};
// Validate before adding new color
function addColor(newColor, purpose) {
const existing = Object.values(palette).flat();
const similar = existing.find(c => colorDistance(c, newColor) < 30);
if (similar) {
console.warn(`Color ${newColor} is similar to ${similar}. Use existing?`);
return false;
}
return true;
}Palette Swapping Benefits:
// With organized palette, recoloring is trivial
const enemyVariants = {
normal: { primary: '#4a90d9', secondary: '#2d5a87' },
fire: { primary: '#d94a4a', secondary: '#872d2d' },
ice: { primary: '#4ad9d9', secondary: '#2d8787' }
};
// At runtime, just swap the palette index
function recolorSprite(sprite, variant) {
sprite.setPalette(enemyVariants[variant]);
}Tools:
- Aseprite: Indexed color mode, palette editor
- Lospec: Curated palettes to start from
- GraphicsGale: Palette animation support
References
- Pixel art color theory
Outline Thickness Varies Across Sprites
Id
inconsistent-outline-weight
Severity
MEDIUM
Description
Some sprites have 1px outline, some 2px, some none
Symptoms
- Art looks inconsistent
- Some characters feel "heavier" than others
- Style seems mixed
- Professional look is lost
Detection Pattern
outline|border|edge|line
Solution
Outline Consistency Rules:
Choose One Approach:
no_outline:
description: No black/dark outline
style: "Softer, more painted look"
best_for: Larger sprites, atmospheric games
example: "Hyper Light Drifter"
selective_outline:
description: Outline only on outside edge
style: "Clean, readable"
best_for: Most games
example: "Celeste"
full_outline:
description: Outline on all edges including internal
style: "Cartoon, bold"
best_for: Action games, visibility important
example: "Shovel Knight"
colored_outline:
description: Outline matches nearby colors (not black)
style: "Soft, integrated"
best_for: Artistic games
example: "Stardew Valley"Implementation:
if_using_outlines:
weight: 1px always (at native resolution)
color:
option_1: Pure black (#000000)
option_2: Near-black (#1a1a2e)
option_3: Darkest shade of object's color
apply_to:
- All character sprites
- All interactable objects
- NOT: Backgrounds (usually)
- NOT: Particle effects (usually)
exceptions:
glow_effects: No outline
transparent_objects: Lighter outline or none
distant_backgrounds: No outlineChecklist Before Export:
[ ] All characters have same outline style
[ ] All enemies have same outline style
[ ] All props have same outline style
[ ] Outline weight is consistent (1px)
[ ] Outline color is consistent or logically varied
[ ] UI elements follow their own consistent rulesReferences
- Pixel art outline techniques
Wrong Export Format Destroys Pixel Art
Id
export-format-destroys-art
Severity
MEDIUM
Description
JPEG compression or wrong PNG settings ruin sprites
Symptoms
- Artifacts around edges
- Colors shifted from original
- File size unexpectedly large or small
- Transparency doesn't work correctly
Detection Pattern
export|save|format|png|jpeg|gif
Solution
Correct Export Settings:
Never Use:
jpeg: Never for pixel art (lossy compression adds artifacts)
webp_lossy: Never for pixel art (same issue)
gif: Limited to 256 colors, no semi-transparency
svg: Wrong format entirely (vector, not raster)Always Use:
png:
color_type: "Indexed (8-bit) if < 256 colors"
alternative: "RGBA if need semi-transparency"
compression: "Maximum (lossless, just smaller file)"
interlace: "None (smaller file)"
aseprite_settings:
indexed_color_mode: true
export_as: PNG
trim_cels: false # Keep consistent frame size
split_layers: false # Unless intentionalSprite Sheet Export:
// Aseprite CLI export (batch script)
const asepriteExport = `
aseprite -b input.ase \\
--sheet output.png \\
--sheet-type packed \\
--sheet-pack \\
--data output.json \\
--format json-array \\
--list-tags \\
--list-slices
`;
// Verify export integrity
function verifyExport(originalPath, exportedPath) {
// Load both, compare pixel-by-pixel
// Any difference = potential corruption
}File Size Expectations:
typical_sizes:
16x16_sprite: 0.5-2 KB (indexed PNG)
32x32_sprite: 1-4 KB
sprite_sheet_256x256: 10-30 KB
sprite_sheet_512x512: 30-100 KB
if_much_larger:
- Check if saving as RGBA instead of indexed
- Check for hidden layers/frames
- Verify PNG compression levelReferences
- Pixel art export best practices
Pixel Art Sprites - Validations
Pixel Art Rendering Mode
Id
check-pixel-art-mode
Description
Game should use pixelated/crisp rendering
Pattern
pixelArt|antialias|roundPixels|image-rendering
File Glob
*/.{js,ts,css}
Match
present
Context Pattern
true|pixelated|crisp-edges
Message
Enable pixel-perfect rendering (pixelArt: true, antialias: false)
Severity
error
Autofix
Integer Scaling
Id
check-integer-scaling
Description
Sprites should use integer scale values
Pattern
setScale|scale\s\(|transform.scale
File Glob
*/.{js,ts}
Match
present
Context Pattern
\b[1-9]\b|Math\.floor|Math\.round|\|\s*0
Message
Use integer scaling for pixel art (1x, 2x, 3x, not 1.5x)
Severity
warning
Autofix
Sprite Sheet Configuration
Id
check-sprite-sheet-config
Description
Sprite sheets should specify frame dimensions
Pattern
spritesheet|load.*sprite|atlas
File Glob
*/.{js,ts}
Match
present
Context Pattern
frameWidth|frameHeight|frame.*[Ww]idth
Message
Specify frameWidth and frameHeight for sprite sheets
Severity
error
Autofix
Animation Timing Values
Id
check-animation-timing
Description
Animations should have explicit timing/duration
Pattern
anims\.create|createAnimation|animation
File Glob
*/.{js,ts}
Match
present
Context Pattern
duration|frameRate|delay
Message
Specify animation timing (duration or frameRate)
Severity
warning
Autofix
PNG Format for Sprites
Id
check-png-format
Description
Sprite assets should be PNG format
Pattern
load\.image|load\.spritesheet|load\.atlas|src.*=
File Glob
*/.{js,ts,html}
Match
present
Context Pattern
\.png|\.PNG
Message
Use PNG format for pixel art sprites (not JPEG)
Severity
warning
Autofix
No JPEG Sprites
Id
check-no-jpeg-sprites
Description
JPEG should not be used for pixel art
Pattern
\.(jpg|jpeg|JPG|JPEG)
File Glob
*/.{js,ts,html}
Match
absent
Message
JPEG destroys pixel art - use PNG instead
Severity
error
Autofix
Consistent Frame Sizes
Id
check-consistent-frame-size
Description
All frames in animation should be same size
Pattern
frameWidth|frame_width|spriteWidth
File Glob
*/.{js,ts,json}
Match
present
Message
Ensure all animation frames have consistent dimensions
Severity
info
Autofix
Animation Loop Configuration
Id
check-animation-loop-config
Description
Animations should specify repeat behavior
Pattern
anims\.create|createAnimation
File Glob
*/.{js,ts}
Match
present
Context Pattern
repeat|loop|yoyo
Message
Specify animation repeat behavior (loop, once, yoyo)
Severity
info
Autofix
Related skills
How it compares
Pick pixel-art-sprites for grid-based 2D game art rules; pick general illustration skills when output is high-resolution concept art without pixel constraints.
FAQ
What does pixel-art-sprites do?
Specialist in pixel art creation, sprite animation, and limited palette designUse when "pixel art, sprites, sprite sheet, sprite animation, retro game art, 8-bit, 16-bit, character sprites, tile art,
When should I use pixel-art-sprites?
During build frontend work for game development.
Is pixel-art-sprites safe to install?
Review the Security Audits panel on this listing before production use.