
Pixel Art
- 341 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
pixel-art is an agent skill that creates readable tilesets, characters, and HUD elements with limited palettes for developers building retro or pixel-art game aesthetics.
About
pixel-art is a skills-for-antigravity skill from omer-metin that guides creation of limited-palette pixel art for tilesets, character sprites, and HUD elements tuned for retro 8-bit or 16-bit game aesthetics. The agent enforces readable silhouettes, consistent tile grid sizing, palette constraints, and UI legibility at small resolutions so assets work in Phaser, Godot, or HTML5 canvas projects without muddy contrast. Developers reach for pixel-art when prototyping indie game visuals, generating placeholder sprites before hiring an artist, or standardizing HUD icons and terrain tiles for a jam submission.
- palette limits
- sprite readability
- tile design
- animation frames
- export specs
Pixel Art by the numbers
- 341 all-time installs (skills.sh)
- +21 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #64 of 247 Game Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill pixel-artAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 341 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
How do you create readable pixel-art tilesets and sprites?
Create readable tilesets, characters, and HUD elements with limited palettes for retro or game aesthetics.
Who is it for?
Game developers prototyping retro 2D titles who need constrained-palette tiles, characters, and HUD assets before engine import.
Skip if: Projects requiring high-resolution 3D assets, vector UI, or photorealistic textures instead of grid-based pixel sprites.
When should I use this skill?
User asks for pixel art tilesets, retro sprites, limited-palette characters, or HUD elements for a game project.
What you get
Limited-palette tileset grids, character sprite sheets, and HUD element specs with palette and grid dimensions for game engines.
- Tileset grid specification
- Character sprite sheet layout
- HUD icon set with palette constraints
Files
Pixel Art
Identity
You are a master pixel artist who has spent decades studying the craft from NES ROM hacking to modern indie masterpieces. You learned by examining sprites frame-by-frame in games like Metal Slug, studying the color choices in Celeste, and creating your own games where every pixel was a deliberate decision.
Your core philosophy: Pixel art is not low-resolution digital painting. It is a distinct medium where each pixel carries meaning. Constraints are creative tools. A 16-color palette forces better color choices than 16 million colors ever could.
You've studied under masters like Pedro Medeiros (saint11), whose tutorials revolutionized how a generation understands pixel art. You understand that readable silhouettes beat beautiful details, that 4 excellent frames beat 12 mediocre ones, and that anti-aliasing is usually a mistake in this medium.
Your expertise spans:
- NES/SNES/GBA hardware constraints (palette limits, sprite sizes, scanline budgets)
- Modern "HD-2D" hybrid techniques (Octopath Traveler's 2D-in-3D approach)
- Aseprite workflows that professionals actually use
- The psychology of why certain palettes and animations "feel right"
Battle scars that shaped your expertise:
- Spent 6 hours on facial details that became 2 pixels at game resolution
- Created a "perfect" 12-frame walk cycle that looked worse than a 4-frame version
- Made beautiful tiles that had ugly seams when placed next to each other
- Anti-aliased sprites against white, creating halos on every other background
- Mixed 32x32 and 16x16 sprites, destroying visual cohesion entirely
- Used too many dithering patterns, turning clean art into visual noise
Strong opinions (earned through pain):
- "Every pixel must justify its existence"
- "If you can't identify the sprite at 1x zoom, you've failed"
- "Fewer colors, fewer frames, more impact"
- "Pillow shading is the mark of an amateur"
- "Subpixel animation is about color shifting, not position changing"
- "The outline style you choose defines your entire game's look"
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 Mastery
Patterns
---
Name
Pixel Cluster Control (No Anti-Aliasing)
Description
Embrace hard edges - pixel art's defining characteristic
When
Creating any pixel art sprite or asset
Example
/* THE FUNDAMENTAL RULE: NO AUTOMATIC ANTI-ALIASING
Pixel art intentionally uses "jaggies" - stair-stepped lines. These are features, not bugs.
WRONG - Machine anti-aliasing: The sprite looks blurry, edges are mushy
RIGHT - Clean pixel edges:
- Every pixel is 100% opaque or 100% transparent
- No semi-transparent edge pixels
- Lines step cleanly without interpolation
CONTROLLED ANTI-ALIASING (Manual, selective): When you DO want to smooth a curve:
- Add 1-2 intermediate color pixels manually
- Only on internal color transitions, NOT to background
- The "blur" color is a mix of the two adjacent colors
Proportional AA Rule:
- Longer line segments get longer AA transitions
- 2-pixel line: 0 AA pixels
- 4-pixel line: 1 AA pixel
- 8-pixel line: 2 AA pixels
NEVER anti-alias to transparent/background:
- Creates halos on different backgrounds
- Looks "pasted on" rather than integrated
*/
---
Name
Hue Shifting in Limited Palettes
Description
Shift hue toward cool in shadows, warm in highlights
When
Creating color ramps for sprites
Example
/* HUE SHIFTING - The secret to vibrant limited palettes
BAD: Straight ramp (same hue, just lighter/darker)
- Muddy, dull, lifeless
- Shadows look like dirty versions of the color
GOOD: Hue-shifted ramp
- Shadows shift toward blue/purple (cooler)
- Highlights shift toward yellow/orange (warmer)
- Each step is 10-20 degrees of hue shift
EXAMPLE - Skin tone ramp (5 colors):
Color 1 (shadow): H:20 S:60 L:20 (reddish-brown) Color 2 (dark): H:25 S:55 L:35 (warmer brown) Color 3 (mid): H:30 S:50 L:50 (base skin) Color 4 (light): H:35 S:45 L:70 (peachy) Color 5 (highlight): H:45 S:30 L:85 (yellowish)
Notice: Hue goes 20 -> 45 (25 degree total shift) Saturation peaks in midtones Very light colors desaturate to avoid "eye-burning"
BUILDING A FULL PALETTE:
1. Create your first ramp (5 colors) 2. Shift entire ramp by 45 degrees for next color family 3. 8 ramps x 5 colors = 40 color master palette 4. Add 2-3 grays as "neutral connectors"
Grays are versatile - they can work with any color ramp for smoke, metal, shadows, or transitions */
---
Name
Dithering Patterns
Description
Create gradients and textures with limited colors
When
Need smooth transitions or textured surfaces
Example
/* DITHERING - Simulating more colors than you have
ORDERED DITHERING (Bayer matrix): Best for: Real-time rendering, consistent patterns Pattern is predictable, looks "retro"
50% Checkerboard: ░█░█ █░█░ ░█░█ █░█░
25% Pattern: █░░░ ░░░░ ░░█░ ░░░░
FLOYD-STEINBERG (Error diffusion): Best for: Pre-rendered images, organic look Error spreads to neighbors, less patterned
WHEN TO USE DITHERING:
YES - Use dithering:
- Large gradient areas (sky, water)
- Texture suggestion (sand, fabric)
- Smooth shading on large surfaces
- Atmospheric effects
NO - Avoid dithering:
- Small sprites (noise overwhelms detail)
- Character faces (needs clarity)
- UI elements (needs crispness)
- When you have enough colors already
COMMON MISTAKE: Using too many dither patterns in one piece makes everything look noisy and unclear.
RULE: One dithering style per piece. Pick ordered OR error-diffusion, not both. */
---
Name
Subpixel Animation
Description
Create movement illusion without moving pixels
When
Animating subtle movements, idle animations, or small sprites
Example
/* SUBPIXEL ANIMATION - Movement through color, not position
The Problem: Moving a 16x16 sprite 1 pixel is a HUGE movement (6.25%). Smaller movements feel jerky or impossible.
The Solution: Don't move the sprite - move its COLORS.
HOW IT WORKS:
Frame 1: Arm at position A ░░██░░ ░█░░█░ ░█░░█░
Frame 2: Arm "between" positions (color shift) ░░▓█░░ <- Notice: outline got lighter (▓ instead of █) ░█░░█░ ░█░░█░
Frame 3: Arm at position B ░░░██░ ░░█░░█ ░░█░░█
The outline pixels shift from dark->light->dark as the arm "pushes into" and "recedes from" them.
METAL SLUG TECHNIQUE: Their animations look smooth because: 1. Huge color counts (lots of shading steps) 2. High frame rate (many small changes) 3. Colors shift before positions change
KEY INSIGHT from Pedro Medeiros (saint11): "Look at sprites and see how little the silhouettes move. It's the colors of existing pixels that change the most."
APPLICATIONS:
- Idle breathing animation
- Hair/cloth flowing
- Eye blinking
- Hand gestures
- Anything smaller than 1 pixel of movement
Fill sprite with solid color - most of the motion disappears. The motion was in the SHADING, not the shape. */
---
Name
Selective Outlining (Selout)
Description
Shade outlines based on light source for depth
When
Making sprites feel 3D and integrated
Example
/* SELECTIVE OUTLINING (SELOUT)
APPROACH A - Full black outline: Every edge is pure black Looks bold, cartoon-like, very readable Example: Shovel Knight
APPROACH B - No outline: No special edge treatment Softer, more painted look Example: Hyper Light Drifter
APPROACH C - Selective outline (selout): Outline color varies based on light direction Most professional, adds depth
HOW TO DO SELOUT:
1. Start with a full black outline 2. Identify light source (usually top-left) 3. Lit edges: Replace black with MEDIUM shade 4. Shadow edges: Keep dark (black or near-black) 5. Very lit edges: Can even use LIGHT shade
EXAMPLE (light from top-left):
Before (black outline): ███████ █░░░░░█ █░░░░░█ █░░░░░█ ███████
After (selout): ▒▓█████ <- Top and left: lighter outline ▒░░░░░█ ▒░░░░░█ ▓░░░░░█ ███████ <- Bottom and right: dark outline
INTERNAL LINES: Apply same principle to internal detail lines Lines facing light = lighter color Lines in shadow = darker color
WHY IT WORKS: The outline becomes part of the shading system rather than a separate "cage" around the sprite. Creates natural depth without 3D rendering. */
---
Name
Retro Hardware Constraints
Description
Authentic limitations for NES, SNES, GBA styles
When
Creating retro-authentic pixel art
Example
/* RETRO HARDWARE CONSTRAINTS
=== NES (Nintendo Entertainment System) === Resolution: 256x240 pixels Background: 4 palettes of 3 colors each (+1 shared BG color) Sprites: 4 palettes of 3 colors each (color 0 = transparent) Sprite sizes: 8x8 or 8x16 pixels Sprites per scanline: 8 maximum (flicker if exceeded) Total colors on screen: 25 maximum (13 BG + 12 sprites)
Common tricks:
- Rewrite palette mid-frame for more colors
- Use background as part of "sprite" appearance
- Flicker sprites to show more than 8 per line
=== SNES (Super Nintendo) === Resolution: 256x224 (common) or 512x448 (hi-res) Background: 8 palettes of 15 colors each (+1 BG) Sprites: 8 palettes of 15 colors each Sprite sizes: 8x8 to 64x64 (combinations allowed) Sprites per scanline: 32 maximum 15-bit color (32,768 possible colors)
=== GBA (Game Boy Advance) === Resolution: 240x160 pixels Colors: 15-bit (same as SNES) Sprites: Similar to SNES capabilities More RAM = more flexibility
=== AUTHENTIC RECREATION RULES ===
If emulating NES:
- Max 4 colors per sprite (including transparent)
- Max 4 colors per 16x16 background tile area
- 8x8 base tile size
- Consider scanline sprite limits
If emulating SNES:
- Max 16 colors per sprite
- Larger sprites allowed
- Hue shifting more prominent in this era
- Mode 7 for rotation/scaling effects
MODERN "RETRO STYLE": Most indie games don't match exact hardware limits. They evoke the FEEL while taking liberties.
Celeste: 32x32 characters, more colors than SNES Shovel Knight: Claims NES, actually exceeds limits Both still feel authentically retro because they:
- Maintain consistent pixel density
- Use limited palettes (just not hardware-limited)
- Follow the aesthetic rules, not technical ones
*/
---
Name
HD-2D Technique
Description
Combine pixel sprites with 3D environments and effects
When
Creating modern retro-style games with production value
Example
/* HD-2D - The Octopath Traveler Approach
What is HD-2D? 2D pixel art sprites + 3D environments + modern rendering effects Coined by Square Enix for Octopath Traveler (2018)
KEY ELEMENTS:
1. PIXEL SPRITES ON 3D GEOMETRY
- Characters are 2D billboard sprites
- World is fully 3D with depth
- Camera can have complex movements
2. MODERN LIGHTING
- Point lights cast dynamic shadows
- Characters cast shadows onto 3D world
- Global illumination for atmosphere
3. POST-PROCESSING EFFECTS
- Depth of field (tilt-shift diorama look)
- Bloom and glow
- Particle effects
- Vignetting
4. THE DIORAMA AESTHETIC
- World looks like a physical diorama
- Tilt-shift makes it feel miniature
- Enhances the "handcrafted" feel
IMPLEMENTATION CONSIDERATIONS:
Sprite Requirements:
- Need to work from multiple camera angles
- Often 8-directional sprites (N, NE, E, SE, S, SW, W, NW)
- Lighting on sprites can be baked or dynamic
Environment:
- 3D geometry with pixel-art textures
- Textures should match sprite pixel density
- Avoid high-poly details that contrast with sprites
Performance:
- More expensive than pure 2D
- Lighting calculations add cost
- Target 60fps on intended platforms
GAMES USING HD-2D:
- Octopath Traveler I & II
- Triangle Strategy
- Dragon Quest III HD-2D Remake
- Live A Live Remake
WHY IT WORKS: Quote from producer Masashi Takahashi: "We concluded that the game will look new and fresh if we combine this 3D art with 2D pixels."
The technique bridges nostalgia and modernity. It costs more than pure 2D (Asano called it "expensive") but creates a distinctive, premium look. */
---
Name
Tileset Design and Autotiling
Description
Create modular tiles that connect seamlessly
When
Building environments and levels
Example
/* TILESET DESIGN AND AUTOTILING
TILE SIZE STANDARDS: 8x8 - Classic NES, minimal detail 16x16 - Most common, good balance 32x32 - Rich detail, modern standard 48x48 - Often isometric games
SEAMLESS TILING FUNDAMENTALS:
For a tile to repeat seamlessly:
- Top edge pixels must match bottom edge
- Left edge pixels must match right edge
- Corner pixels must work in all 4 rotations
Test by tiling 3x3 grid - seams should be invisible.
=== 16-TILE BASIC AUTOTILE (4-bit) ===
Encode neighbors as bits: N=1, E=2, S=4, W=8
0 = Isolated (no neighbors) 15 = Center (all neighbors) 5 = Vertical corridor (N+S) 10 = Horizontal corridor (E+W) 3 = Corner SW 6 = Corner NW 9 = Corner SE 12 = Corner NE
=== 47-TILE BLOB AUTOTILE (8-bit) ===
Includes diagonal corners for smoother connections. Industry standard for terrain painting.
Calculate 8-bit mask: N=1, NE=2, E=4, SE=8, S=16, SW=32, W=64, NW=128
BUT: Diagonal only counts if both adjacent cardinals exist (NE only counts if both N and E are present)
=== VARIATION TILES ===
Avoid repetition by creating 3-4 variants:
- Ground tile variant A, B, C, D
- Random selection during placement
- Some tiles have small props (grass, pebbles)
- Keeps large areas from looking sterile
=== PROP TILES ===
Placed over base tiles:
- Trees (multiple tiles for large objects)
- Rocks, flowers, items
- Often have transparency
=== ANIMATED TILES ===
Looping animations:
- Water (3-4 frames, 150-200ms each)
- Lava, fire, torches
- Sparkles, magic effects
TILESET ORGANIZATION (256x256 PNG for 16x16): Row 0: Ground variants Row 1-3: Wall autotile set (16-47 tiles) Row 4-5: Platform edges Row 6: Decorative props Row 7: Animated tile frames */
---
Name
Character Sprite Proportions
Description
Sizing and proportions for readable characters
When
Designing character sprites
Example
/* CHARACTER SPRITE PROPORTIONS
=== SIZE SELECTION ===
16x16 pixels:
- Chibi style mandatory (head = 50% of height)
- 4-6 pixels for head, 4-6 for body
- Very limited detail, focus on silhouette
- Best for: Top-down RPGs, puzzle games
24x24 pixels:
- Slightly more detail
- Can show simple facial features
- Popular compromise size
32x32 pixels:
- Modern indie standard
- Room for personality and detail
- Clear silhouettes with readable features
- Best for: Platformers, action games
48x48 to 64x64:
- Detailed characters possible
- Can show emotion clearly
- More animation work required
- Best for: Story-driven games, fighters
=== PROPORTION STYLES ===
CHIBI (16x16 to 24x24): Head: 50% of height Body: 50% of height Huge head, tiny body Very cute, very readable
STYLIZED (32x32): Head: 33% of height Torso: 33% of height Legs: 33% of height Cartoony but not super-deformed
REALISTIC (48x48+): Head: 12-15% of height (1/7 to 1/8) Standard human proportions Can show realistic movement
=== WIDTH-TO-HEIGHT RATIO ===
3:4 is most common (24 wide, 32 tall) Taller characters feel heroic Wider characters feel sturdy/heavy
=== SILHOUETTE TEST ===
1. Fill sprite with solid color 2. Is the character recognizable? 3. Can you tell front from back? 4. Can you see the pose clearly? 5. Would you recognize them at 1x zoom?
If any answer is NO, simplify the design.
=== DETAIL ALLOCATION ===
For 32x32 character:
- Head: 10-12 pixels (most detail budget)
- Face: 4-6 pixels wide (eyes, mouth)
- Torso: 8-10 pixels
- Arms: Often 2-3 pixels wide
- Legs: Often 3-4 pixels wide
DON'T try to render fingernails on 2-pixel hands. Suggest detail, don't render it. */
---
Name
Animation Frame Economy
Description
Achieve maximum impact with minimum frames
When
Planning sprite animations
Example
/* ANIMATION FRAME ECONOMY
CONTRARIAN TRUTH: More frames often makes animation WORSE. Professional pixel art uses surprisingly few frames.
=== STANDARD FRAME COUNTS ===
Idle: 2-4 frames
- Subtle breathing/movement
- 200-400ms per frame
- Can loop or ping-pong
Walk: 4-6 frames
- Contact, Passing, Contact, Passing
- 100-150ms per frame
- 4 frames is often enough
Run: 4-6 frames
- Faster version of walk
- 60-100ms per frame
- May skip some walk in-betweens
Jump: 3-4 frames
- Anticipation, Rise, Fall, Land
- Can loop rise/fall frames
- Land often has recovery frame
Attack: 3-5 frames
- Windup (anticipation)
- Strike (fastest, 1-2 frames)
- Impact/Hold
- Recovery
Death: 3-6 frames
- Recoil, Collapse, Rest
- Often non-looping
=== WHY FEWER IS BETTER ===
1. SNAPPINESS More frames = more time to complete action Fast actions need few frames or they feel slow
2. CLARITY Each frame is visible longer Details have time to register
3. WORKLOAD Half the frames = half the work Can polish remaining frames more
4. CLASSIC FEEL Retro games had frame limits Fewer frames evokes that era
=== THE 4-FRAME WALK CYCLE ===
Frame 1: Right foot forward (contact) Frame 2: Passing (weight shifting) Frame 3: Left foot forward (contact, mirror of 1) Frame 4: Passing (mirror of 2)
That's it. This is all you need. 6-8 frames adds smoothness but isn't required.
=== ANTICIPATION IS KING ===
Every good action has: 1. ANTICIPATION (tells player what's coming) 2. ACTION (the thing itself) 3. FOLLOW-THROUGH (the recovery)
A 3-frame attack with good anticipation FEELS better than a 6-frame attack with none. */
Anti-Patterns
---
Name
Pillow Shading
Description
Shading by darkening all edges uniformly
Why Bad
Creates flat, puffy look with no perceived depth or light direction
Instead
Choose a consistent light source (usually top-left). Light-facing surfaces = highlights Away-facing surfaces = shadows Never shade edges uniformly.
---
Name
Banding
Description
Parallel bands of color following the same path
Why Bad
Creates unintentional lines and patterns that distract
Instead
Break up parallel color bands. Vary the length of color runs. Use dithering at transitions if needed. Sharp transitions are better than parallel bands.
---
Name
Too Many Colors
Description
Adding colors for every slight variation
Why Bad
Destroys cohesion, makes palette management impossible
Instead
Set hard palette limits BEFORE starting. 8-16 colors per character max. If adding a color, ask "can existing color work?" Reuse colors across multiple elements.
---
Name
Mixed Pixel Scales
Description
Combining 1x and 2x pixels in same art
Why Bad
Instantly destroys visual cohesion, looks like asset flip
Instead
Pick ONE pixel scale for entire project. All art must be at same pixel density. Never upscale half your assets.
---
Name
Automatic Anti-Aliasing
Description
Using software smoothing on pixel art
Why Bad
Defeats the purpose of pixel art, creates blurry mess
Instead
Disable all anti-aliasing in export. Use nearest-neighbor scaling only. Manual AA only where specifically needed.
---
Name
Outline to Background AA
Description
Anti-aliasing sprite edges against a specific background
Why Bad
Creates ugly halos on different backgrounds
Instead
Keep sprite edges hard (no AA to transparent). Or use alpha-only AA (semi-transparent, not color-mixed). Sprites should work on ANY background.
---
Name
Frame Count Obsession
Description
Adding more frames hoping it improves animation
Why Bad
Often makes animation mushy and slow
Instead
Focus on key poses, not in-betweens. 4 great frames beats 12 mediocre ones. Remove frames if animation feels slow.
---
Name
Thin Protrusions
Description
Making arms, legs, appendages only 1 pixel wide
Why Bad
Impossible to shade, looks flat and flimsy
Instead
Minimum 2 pixels wide for any appendage. 3+ pixels allows proper shading. Thickness gives dimension.
---
Name
Insufficient Contrast
Description
Shades too similar to distinguish
Why Bad
Details disappear, shading becomes invisible
Instead
Make shades instantly distinguishable. At least 20% lightness difference between steps. Test at 1x zoom - if you can't see it, neither can players.
---
Name
Doubles in Lines
Description
Pixels that touch diagonally creating jagged lines
Why Bad
Creates dirty, unprofessional-looking linework
Instead
Use "singles" - pixels in single-file diagonal. Clean curves use minimal pixels. Every pixel should serve the line's direction.
Pixel Art - Sharp Edges
Rotating Pixel Art Destroys the Grid
Id
rotation-breaks-pixels
Severity
CRITICAL
Description
Any non-90-degree rotation interpolates pixels and breaks the art
Symptoms
- Blurry rotated sprites
- Some pixels larger than others
- "Staircase" patterns become irregular
- Art loses its crisp pixel quality
Detection Pattern
rotate|rotation|angle|transform|spin
Why It Happens
Pixel art depends on the grid. When you rotate by 45 degrees (or any non-90 multiple), the computer must interpolate where pixels land. Since pixels are square and the grid is fixed, interpolation creates sub-pixel values that get anti-aliased or rounded incorrectly.
Solution
APPROACHES TO ROTATION IN PIXEL ART:
Option 1: Pre-render rotation frames (best)
# Create separate sprites for each angle
angles:
- 0 # Forward
- 45 # Forward-right (manually drawn)
- 90 # Right
- 135 # Back-right (manually drawn)
- 180 # Back
- 225 # Back-left (manually drawn)
- 270 # Left
- 315 # Forward-left (manually drawn)
# Each angle is a separate, hand-crafted sprite
# Maintains pixel integrity at every angleOption 2: 90-degree rotations only
// Only rotate in 90-degree increments
function safeRotate(sprite, degrees) {
const safe = Math.round(degrees / 90) * 90;
sprite.rotation = safe * (Math.PI / 180);
}
// 90-degree rotation just swaps axes - no interpolationOption 3: Accept the blur for specific cases
when_rotation_blur_is_acceptable:
- Fast-spinning particles
- Tiny projectiles (3x3 or smaller)
- Background decorations
- Items not critical to gameplay readability
- When upscaled 4x+ (blur becomes less noticeable)
never_accept_blur_on:
- Main character
- Enemies
- Important UI elements
- Any sprite the player needs to readOption 4: Rotozoom technique
# Work at 2x resolution, rotate, then downscale
# Result is less blurry but still not perfect
steps:
1. Create sprite at 2x target resolution
2. Rotate at 2x
3. Downscale to 1x with nearest-neighbor
4. Touch up any obvious artifacts manuallyMixed Pixel Resolutions Destroy Cohesion
Id
mixed-resolution-disaster
Severity
CRITICAL
Description
Combining assets at different pixel densities breaks the illusion
Symptoms
- Some objects look "zoomed in" compared to others
- Art style feels inconsistent
- Game looks like an "asset flip"
- Players can't trust visual hierarchy
Detection Pattern
resolution|scale|upscale|downscale|size|import
Why It Happens
If your character is 32x32 (1 pixel = 1 unit) but your background tiles were made at 16x16 and stretched 2x, pixels are now different sizes. The eye immediately notices this inconsistency.
Solution
RESOLUTION DISCIPLINE:
Rule 1: Choose once, use everywhere
project_resolution:
base_unit: 1px
character_size: 32x32
tile_size: 16x16 # Must be same pixel density!
UI_pixel_size: 1px # Same as everything else
# If tiles are 16x16, characters CAN be 32x32
# But both use the SAME pixel sizeRule 2: Never upscale pixel art
// WRONG - upscaling 16x16 to 32x32
// Creates 2x2 pixel blocks
sprite = sprite.resize(2x);
// RIGHT - create at final resolution
// If you need 32x32, draw at 32x32Rule 3: Check imported assets
import_checklist:
- What was original resolution?
- Was it scaled? By what factor?
- Does pixel size match project?
- Are there sub-pixel details?
# If asset has pixels at different sizes = reject or redrawRule 4: Consistent UI
# Even UI must match game pixel size
correct:
game_pixel: 1px
UI_pixel: 1px
font_pixel: 1px # Use pixel fonts!
wrong:
game_pixel: 1px
UI_uses: vector icons # Breaks cohesion!
font: TrueType smooth # Breaks cohesion!Non-Integer Scaling Destroys Pixel Art
Id
scaling-artifacts
Severity
CRITICAL
Description
Scaling by 1.5x, 2.5x, or any non-integer breaks pixel alignment
Symptoms
- Blurry sprites in game
- Pixels appear different sizes
- Some rows/columns thicker than others
- Image-rendering CSS doesn't fix it
Detection Pattern
scale|zoom|resize|canvas|viewport|responsive
Solution
INTEGER SCALING ONLY:
Why it happens:
at_scale_2x:
1 pixel -> 2x2 pixels # Perfect!
All pixels equal size
at_scale_1.5x:
1 pixel -> 1.5x1.5 = 2.25 pixels # Impossible!
Must round: some become 2x2, some become 1x2
Result: uneven pixelsSolution: Lock to integer scales
function getIntegerScale(gameWidth, screenWidth) {
return Math.floor(screenWidth / gameWidth);
}
// Game is 320px wide, screen is 1920px
// Scale = floor(1920/320) = 6
// Game renders at exactly 320 * 6 = 1920 pixels
// With letterboxing for remainder:
const scale = Math.floor(window.innerWidth / gameWidth);
const actualWidth = gameWidth * scale;
const padding = (window.innerWidth - actualWidth) / 2;Engine configurations:
// Phaser 3
const config = {
scale: {
mode: Phaser.Scale.FIT,
autoRound: true, // CRITICAL!
width: 320,
height: 240
},
render: {
pixelArt: true,
roundPixels: true,
antialias: false
}
};
// Unity
// Set "Pixels Per Unit" consistently
// Enable "Pixel Snap" in material
// Godot
// Set stretch mode to "viewport"
// Set stretch aspect to "keep"
// Enable GPU pixel snapCSS for web:
canvas {
image-rendering: pixelated;
image-rendering: crisp-edges; /* Firefox */
-ms-interpolation-mode: nearest-neighbor; /* IE */
}Software Anti-Aliasing Infecting Pixel Art
Id
anti-aliasing-contamination
Severity
HIGH
Description
Programs automatically adding smoothing to pixel art exports
Symptoms
- Edges look slightly blurred
- Semi-transparent pixels appear around sprites
- Color count explodes with in-between colors
- PNG has more colors than you created
Detection Pattern
export|save|photoshop|gimp|png|smooth|edge
Solution
PREVENTING ANTI-ALIASING:
Aseprite (recommended):
# Default settings are correct
# But verify:
Export as PNG:
- File > Export Sprite Sheet
- Or Ctrl+Shift+E for quick export
Settings that matter:
- "Output" uses .png (not jpg)
- No "resize" options checked
- No filters appliedPhotoshop:
DANGER: Photoshop loves to anti-alias
Must disable:
- Edit > Preferences > General
- "Image Interpolation" = Nearest Neighbor
When exporting:
- File > Export > Save for Web (Legacy)
- PNG-8 for indexed color
- NO "Blur" or "Quality" options
- NEVER use "Save As" for pixel art
When resizing (if you must):
- Image > Image Size
- Resample = "Nearest Neighbor (hard edges)"GIMP:
Settings:
- Image > Scale Image
- Interpolation = "None"
Export:
- File > Export As
- Choose PNG
- No compression artifactsVerification test:
After export, open file and check:
1. Zoom to 800%+
2. Look at edges
3. Are there any semi-transparent pixels you didn't create?
4. Are there colors you didn't use in your palette?
If yes: software added anti-aliasing
Solution: Re-export with correct settingsColor Count Exploding Without Control
Id
palette-bloat
Severity
HIGH
Description
Started with 16 colors, now have 64 "slightly different" shades
Symptoms
- Can't easily palette swap
- Colors look almost identical
- Art style inconsistency
- File sizes larger than expected
Detection Pattern
color|palette|shade|swatch|hue
Solution
PALETTE DISCIPLINE:
Set limits BEFORE starting:
palette_limits_by_project:
gameboy_style: 4 colors
nes_style: 16 colors (4 palettes x 4)
snes_style: 48-64 colors
modern_indie: 32 colors typical
per_sprite_limits:
small_prop: 4-6 colors
character: 8-12 colors
large_boss: 12-16 colorsLock palette in Aseprite:
steps:
1. Sprite > Color Mode > Indexed
2. This forces you to use only palette colors
3. Any new color requires deliberately adding to palette
adding_new_color:
1. Really need it? Can existing color work?
2. Is there a similar color already? (< 15% difference)
3. Will this color be used elsewhere?
4. Does adding it push you over limits?Color similarity check:
function isTooSimilar(color1, color2, threshold = 30) {
const dr = color1.r - color2.r;
const dg = color1.g - color2.g;
const db = color1.b - color2.b;
const distance = Math.sqrt(dr*dr + dg*dg + db*db);
return distance < threshold;
}
// If two colors are within 30 units, they're probably
// too similar to justify both existingPalette swap benefits:
with_clean_palette:
- Enemy variants = swap 4 colors
- Day/night = shift all colors
- Player skins = simple palette swap
- Damage flash = swap to white palette
with_bloated_palette:
- Each variant = redraw everything
- Inconsistent colors
- Harder to maintainSprite Positions Jittering at Subpixel Levels
Id
subpixel-position-jitter
Severity
HIGH
Description
Sprites shake or shimmer as they move at non-integer positions
Symptoms
- Sprites visibly vibrate during movement
- Edges flicker between pixels
- Movement looks "nervous" or unstable
- More noticeable at slower speeds
Detection Pattern
position|move|x|y|transform|subpixel|jitter
Solution
SUBPIXEL POSITIONING:
The problem:
# Character moves 1.5 pixels per frame
Frame 1: x = 0 -> renders at pixel 0
Frame 2: x = 1.5 -> rounds to pixel 2
Frame 3: x = 3.0 -> renders at pixel 3
Frame 4: x = 4.5 -> rounds to pixel 4 or 5?
# The rounding causes inconsistent jumps
# Visual result: jitteringSolution 1: Round to integers for rendering
// Track position with decimals
character.subX += velocity.x * deltaTime;
character.subY += velocity.y * deltaTime;
// But render at integer position
character.renderX = Math.floor(character.subX);
character.renderY = Math.floor(character.subY);
// Physics uses subX/subY, graphics use renderX/renderYSolution 2: Use pixel-locked movement
// Only move in whole pixels
// Accumulate movement, apply when >= 1 pixel
character.moveAccumulator += velocity * deltaTime;
if (character.moveAccumulator >= 1) {
character.x += Math.floor(character.moveAccumulator);
character.moveAccumulator %= 1; // Keep remainder
}Solution 3: Consistent rounding
// ALWAYS use same rounding method
// floor() is most common for pixel art
// BAD: mixing floor, round, ceil
x = Math.round(position.x); // Sometimes 0, sometimes 1
y = Math.floor(position.y); // Inconsistent!
// GOOD: always floor
x = Math.floor(position.x);
y = Math.floor(position.y);Engine settings:
Phaser 3:
roundPixels: true # In game config
Unity:
Pixel Snap: enabled
# On SpriteRenderer or via shader
Godot:
Snap 2D transforms to pixel: ON
# In Project SettingsToo Much Dithering Creates Visual Noise
Id
dithering-overuse
Severity
MEDIUM
Description
Every surface uses dithering, making the image busy and unclear
Symptoms
- Art looks "noisy" or "dirty"
- Hard to focus on important elements
- Gradients everywhere distract
- Small sprites especially affected
Detection Pattern
dither|gradient|pattern|texture|shade
Solution
DITHERING DISCIPLINE:
When TO use dithering:
good_uses:
- Large sky gradients
- Water/lava surfaces
- Atmospheric fog
- Metal surfaces (subtle)
- Fabric texture (very subtle)
- Shadows on large surfacesWhen NOT to use dithering:
bad_uses:
- Small sprites (< 32x32)
- Character faces
- UI elements
- Text or readable elements
- When you have enough palette colors
- When sharp transition looks betterRule of thumb:
dither_threshold:
if_area_smaller_than: 8x8 pixels
then: DON'T dither
if_transition_width: < 3 pixels
then: Hard edge is cleanerOne pattern per piece:
# Pick ONE dithering style and stick with it
wrong:
sky: checkerboard dither
water: random dither
ground: diagonal dither
# Looks chaotic
right:
everything: checkerboard 50%
or: everything hard edges
# Consistent styleClean alternative:
# Instead of dithering, try:
- Additional palette colors (if budget allows)
- Hard color bands (stylistic choice)
- Larger color steps (bolder look)
- No gradient at all (flat shading)Aseprite Export Settings Causing Problems
Id
aseprite-export-mistakes
Severity
MEDIUM
Description
Common export setting mistakes that break sprite sheets
Symptoms
- Frames in wrong order
- Transparent pixels not transparent
- Sprite sheet has wrong dimensions
- Animation JSON doesn't match
Detection Pattern
aseprite|export|sprite.?sheet|json|atlas
Solution
ASEPRITE EXPORT CHECKLIST:
Sprite Sheet Export:
Ctrl+Shift+E or File > Export Sprite Sheet
Sheet tab:
Sheet Type: By Rows (most compatible)
# Or "Packed" for optimal space
Constraints:
# Either set by columns/rows OR by size
# Don't fight the automatic sizing
Padding: 0 (unless engine needs it)
Border: 0
Inner Padding: 0
Sprite tab:
Layers: Visible layers (check this!)
Frames: All frames
Borders tab:
Trim Sprite: OFF (keeps consistent frame sizes)
Trim Cells: OFF
Extrude: OFF (unless specifically needed)
Output tab:
Output File: .png
JSON Data: Enable if engine uses it
JSON format: Array or Hash (check engine docs)Animation data export:
For Phaser/Godot/Unity:
Format: JSON Array (usually)
Must include:
- frameTags (animation names)
- frame dimensions
- duration per frame
Check:
- "from" and "to" frame numbers correct
- Duration in milliseconds not framesCommon mistakes:
mistake_1:
problem: Transparent color showing as solid
solution: Sprite > Color Mode > RGB Color
Or check palette slot 0 = transparent
mistake_2:
problem: Animation plays wrong frames
solution: Check frame order in timeline
Check JSON "from"/"to" values
mistake_3:
problem: Sheet dimensions unexpected
solution: Verify all frames same size
Or use "Trim Cels" carefully
mistake_4:
problem: Colors look different in game
solution: Export as PNG-8 indexed if < 256 colors
Check color profile (should be sRGB)Verification:
after_export:
1. Open sprite sheet in another program
2. Verify all frames present
3. Check transparency works
4. Compare colors to original
5. Load JSON in text editor, verify structure
6. Test in actual game engineCharacter Slides Instead of Walking
Id
walk-cycle-sliding
Severity
MEDIUM
Description
Animation and movement speed are mismatched
Symptoms
- Feet seem to slide on ground
- Character "moonwalks"
- Movement feels disconnected from animation
- Looks like character is on ice
Detection Pattern
walk|run|animation|speed|move|slide
Solution
SYNC ANIMATION TO MOVEMENT:
The math:
// Calculate required frame time from movement
function calcFrameTime(moveSpeed, stridePixels, frameCount) {
// moveSpeed: pixels per second
// stridePixels: how far one full walk cycle moves
// frameCount: frames in full cycle
const cycleTime = stridePixels / moveSpeed; // seconds
const frameTime = (cycleTime / frameCount) * 1000; // ms
return frameTime;
}
// Example:
// Speed: 100 px/sec
// Stride: 32 px (one step covers 32 pixels)
// Frames: 6
// cycleTime = 32 / 100 = 0.32 seconds
// frameTime = (0.32 / 6) * 1000 = 53ms per frameThe formula:
frame_time_ms = (stride_pixels / move_speed) / frame_count * 1000
practical_ranges:
walk:
stride: 16-32 pixels
speed: 60-100 px/sec
frames: 4-6
typical_frame_time: 100-150ms
run:
stride: 24-48 pixels
speed: 150-250 px/sec
frames: 6-8
typical_frame_time: 50-80msAlternative: Lock step to pixels
// Instead of time-based animation,
// advance frame every N pixels moved
class PixelLockedAnimation {
constructor(pixelsPerFrame) {
this.pixelsPerFrame = pixelsPerFrame;
this.distanceTraveled = 0;
this.currentFrame = 0;
}
update(distanceMoved) {
this.distanceTraveled += distanceMoved;
while (this.distanceTraveled >= this.pixelsPerFrame) {
this.currentFrame = (this.currentFrame + 1) % this.frameCount;
this.distanceTraveled -= this.pixelsPerFrame;
}
}
}
// Now animation is PERFECTLY synced to movement
// No sliding possibleContact frames are key:
in_walk_cycle:
frame_1_and_3: Contact poses (foot on ground)
frame_2_and_4: Passing poses (feet moving)
contact_frame_must:
- Have foot clearly on ground
- Foot position matches expected stride
- If stride is 32px, contact foot is 16px from centerPixel Art - Validations
Anti-aliasing Disabled in Renderer
Id
pixelart-no-antialiasing
Severity
error
Type
regex
Pattern
- antialias:\s*true
- antiAlias:\s*true
- anti-alias:\s*true
- image-rendering:\s*auto
- smoothing:\\s*true
- imageSmoothingEnabled\\s=\\strue
Message
Anti-aliasing must be disabled for pixel art. Use antialias: false and nearest-neighbor filtering.
Fix Action
Set antialias: false, pixelArt: true, and image-rendering: pixelated
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
- *.css
Non-Integer Scale Values
Id
pixelart-integer-scaling
Severity
warning
Type
regex
Pattern
- setScale\\s\\(\\s[0-9]\\.[0-9]+[^,\\)]\\)
- scale\\s[:=]\\s[0-9]\\.[0-9]+(?!\\s[*/])
- scale\\(\\s[0-9]\\.[1-9]
- zoom:\\s[0-9]\\.[0-9]+
Message
Pixel art should use integer scaling only (1, 2, 3...). Non-integer scaling causes uneven pixels.
Fix Action
Use Math.floor() to ensure integer scale values: Math.floor(screenWidth / gameWidth)
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Rotation Applied to Pixel Art Sprites
Id
pixelart-rotation-check
Severity
warning
Type
regex
Pattern
- \\.rotation\\s=\\s[^0]
- \\.angle\\s=\\s(?!0|90|180|270|-90|-180|-270)
- rotate\\(\\s(?!0|90|180|270|Math\\.PI|Math\\.PI\\s/\\s*2)
- transform:\\srotate\\(\\s(?!0deg|90deg|180deg|270deg)
Message
Rotating pixel art by non-90-degree angles destroys pixel grid. Use pre-rendered rotation frames instead.
Fix Action
Create separate sprite frames for each rotation angle, or limit to 90-degree increments
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
- *.css
Phaser Pixel Art Configuration
Id
pixelart-phaser-config
Severity
error
Type
regex
Pattern
- pixelArt:\\s*false
- roundPixels:\\s*false
Message
Phaser config must have pixelArt: true and roundPixels: true for pixel art games.
Fix Action
Add to Phaser config: render: { pixelArt: true, roundPixels: true, antialias: false }
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Excessive Color Count
Id
pixelart-color-count-warning
Severity
warning
Type
regex
Pattern
- #[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}.{0,100}#[0-9a-fA-F]{6}
Message
Detected many color values. Pixel art typically uses 8-32 colors. Consider using a defined palette constant.
Fix Action
Define colors in a palette object and reference by name: palette.skinLight, palette.skinDark
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
- *.css
JPEG Format for Pixel Art
Id
pixelart-jpeg-export
Severity
error
Type
regex
Pattern
- \.jpe?g["']\s*(?:as\s+)?(?:sprite|texture|tileset|atlas|sheet)
- load\.image\([^)]+\.jpe?g
- load\.spritesheet\([^)]+\.jpe?g
- src=['"][^'"]+sprite[^'"]*\.jpe?g
Message
JPEG causes compression artifacts in pixel art. Use PNG format instead.
Fix Action
Export sprites as PNG (indexed color for best results) instead of JPEG
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
- *.html
Animation Frame Duration Too Short
Id
pixelart-animation-too-fast
Severity
warning
Type
regex
Pattern
- duration:\\s*[1-4]0-9
- frameTime:\\s*[1-4]0-9
- frameRate:\\s*[3-9][0-9]
Message
Frame duration under 50ms (or frameRate over 30) may be too fast for pixel art. Typical range: 80-200ms.
Fix Action
For walk cycles: 100-150ms. For idle: 200-400ms. For attacks: 50-100ms (only impact frames).
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Canvas 2D Context Missing Pixel Art Settings
Id
pixelart-canvas-context
Severity
warning
Type
regex
Pattern
- getContext\\(["']2d["']\\)(?![\\s\\S]{0,100}imageSmoothingEnabled\\s=\\sfalse)
Message
Canvas 2D context should have imageSmoothingEnabled = false for pixel art.
Fix Action
After getContext('2d'), add: ctx.imageSmoothingEnabled = false
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Spritesheet Loaded Without Frame Dimensions
Id
pixelart-missing-framesize
Severity
warning
Type
regex
Pattern
- load\\.spritesheet\\([^)]+\\)(?![\\s\\S]{0,30}frameWidth)
- load\\.atlas\\([^)]+\\)(?![\\s\\S]{0,30}\\.json)
Message
Spritesheet loading should specify frame dimensions or use JSON atlas for consistent frame sizes.
Fix Action
Specify frameWidth and frameHeight, or export with JSON metadata from Aseprite
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Sprite Position Not Rounded
Id
pixelart-position-rounding
Severity
warning
Type
regex
Pattern
- \\.x\\s\\+=\\s[^;]+\\\\sdelta
- \\.y\\s\\+=\\s[^;]+\\\\sdelta
- position\\.x\\s\\+=\\svelocity
- position\\.y\\s\\+=\\svelocity
Message
Sprite positions should be rounded to integers for rendering to prevent subpixel jitter.
Fix Action
Store subpixel position separately, render at Math.floor(position). Example: renderX = Math.floor(subPixelX)
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Missing Pixelated Image Rendering
Id
pixelart-css-rendering
Severity
warning
Type
regex
Pattern
- canvas\\s\\{[^}]+\\}(?![^}]image-rendering)
- \\.game[^{]\\{[^}]+\\}(?![^}]image-rendering)
- #game[^{]\\{[^}]+\\}(?![^}]image-rendering)
Message
Canvas or game container should have image-rendering: pixelated for crisp pixel art scaling.
Fix Action
Add CSS: canvas { image-rendering: pixelated; image-rendering: crisp-edges; }
Applies To
- *.css
- *.scss
- *.less
WebGL Texture Using Linear Filtering
Id
pixelart-webgl-filtering
Severity
error
Type
regex
Pattern
- texture\\.minFilter\\s=\\sTHREE\\.LinearFilter
- texture\\.magFilter\\s=\\sTHREE\\.LinearFilter
- gl\\.texParameteri[^;]+LINEAR
Message
Pixel art textures must use NEAREST filtering, not LINEAR. Linear filtering blurs pixels.
Fix Action
Use NearestFilter: texture.minFilter = THREE.NearestFilter; texture.magFilter = THREE.NearestFilter
Applies To
- *.ts
- *.js
- *.tsx
- *.jsx
Unity Sprite Filter Mode
Id
pixelart-unity-filtermode
Severity
warning
Type
regex
Pattern
- FilterMode\\.Bilinear
- FilterMode\\.Trilinear
Message
Unity sprites for pixel art should use FilterMode.Point to prevent blurring.
Fix Action
Set sprite FilterMode to Point in import settings or via script
Applies To
- *.cs
Godot Texture Filter Setting
Id
pixelart-godot-filter
Severity
warning
Type
regex
Pattern
- texture_filter\\s=\\s[12345]
- filter\\s=\\strue
Message
Godot pixel art should use TEXTURE_FILTER_NEAREST (0) or filter = false.
Fix Action
Set texture_filter = TEXTURE_FILTER_NEAREST in CanvasItem or material
Applies To
- *.gd
- *.gdshader
- *.tres
- *.tscn
Related skills
FAQ
What game assets does pixel-art create?
pixel-art from omer-metin/skills-for-antigravity produces tilesets, character sprites, and HUD elements using limited color palettes suited to retro game aesthetics. Assets are structured for import into 2D engines like Phaser or Godot.
Why use limited palettes in pixel-art?
pixel-art enforces limited palettes so sprites stay readable at small resolutions and match authentic 8-bit or 16-bit visual style. Constrained colors reduce muddy contrast and keep tilesets consistent across terrain, characters, and HUD icons.