21 KiB
Export Formats Reference
This document provides comprehensive technical specifications for exporting pixel art in various formats, including static images, animated formats, spritesheets, and game engine-specific outputs.
PNG Export Options
PNG (Portable Network Graphics) is the primary format for pixel art due to lossless compression and transparency support.
Transparency Handling
Full Alpha Channel:
- 8-bit alpha channel (0-255 transparency levels)
- Preserves semi-transparent pixels from antialiasing or effects
- Essential for overlay sprites, particles, or UI elements
- File size increases with alpha complexity
Binary Transparency:
- Pixels are either fully opaque (255) or fully transparent (0)
- Smaller file size compared to full alpha
- Sufficient for most pixel art with hard edges
- Compatible with older systems
No Transparency:
- All pixels opaque
- Smallest file size for PNG
- Use when background is always solid
- Useful for tiles, backgrounds, or when transparency not needed
Background Colors
Transparent Background:
- Default for sprites, characters, UI elements
- Allows compositing over any background
- Required for multi-layer game objects
Solid Background:
- Specify hex color (e.g., #FF00FF for magenta)
- Useful for preview/reference images
- Can use specific color for manual masking later
- Common for texture atlases with padding
Checkerboard Pattern:
- Not exported, but visible in editor
- Indicates transparent areas during editing
- Final export still uses true transparency
Color Depth Options
Indexed Color (8-bit):
- Maximum 256 colors
- Smallest file size
- Perfect for retro pixel art
- Preserves exact palette
- Example: NES-style art (54 colors), Game Boy (4 colors)
RGB (24-bit):
- 16.7 million colors
- No transparency channel
- Use when alpha not needed
- Moderate file size
RGBA (32-bit):
- 16.7 million colors + alpha
- Full transparency support
- Largest file size
- Industry standard for modern games
Compression Settings
PNG Compression Levels:
- Level 0: No compression (fastest, largest)
- Level 1-5: Low compression (fast encoding)
- Level 6: Default (balanced)
- Level 9: Maximum compression (slowest, smallest)
Best Practices:
- Use level 9 for final game assets (one-time cost)
- Use level 1-3 for rapid iteration/testing
- Compression is lossless at all levels
- File size varies by image complexity, not canvas size
GIF Export
GIF format provides animation support with wide compatibility but limited color palette.
Frame Timing Control
Frame Duration:
- Measured in centiseconds (100ths of a second)
- Minimum: 1cs (10ms) - browser limit typically 20ms
- Common values: 10cs (100ms), 5cs (50ms), 20cs (200ms)
- Per-frame timing allows variable animation speeds
Timing Examples:
Walking cycle: 10cs per frame (10 FPS)
Idle breathing: 30cs per frame (3.3 FPS)
Fast attack: 5cs per frame (20 FPS)
Slow blink: [50cs, 5cs, 50cs] (hold, blink, hold)
Aseprite Frame Duration:
- Set in timeline (milliseconds in Aseprite)
- Export converts to centiseconds
- Maintains exact timing in GIF output
Looping Options
Infinite Loop:
- Default for game sprites
- NETSCAPE2.0 extension with loop count 0
- Plays continuously until stopped
- Standard for web animations
Count-Based Looping:
- Specify exact number of repetitions
- Example: Loop 3 times then stop
- Useful for intro animations, effects
- Browser support varies
No Loop (Play Once):
- Loop count set to 1
- Stops on final frame
- Good for cutscenes, transitions
- Fallback: some viewers may still loop
Color Quantization for GIF
256 Color Limitation:
- GIF maximum palette size
- Quantization reduces full-color to 256 colors
- Critical consideration when exporting from 24-bit source
Quantization Algorithms:
Global Palette:
- Single 256-color palette for all frames
- Ensures color consistency across animation
- Analyze all frames to build optimal palette
- Best for most pixel art animations
Local Palette:
- Different palette per frame
- Allows more colors across animation
- Can cause color shifting/flickering
- Rarely appropriate for pixel art
Dithering Options:
- None: Hard color boundaries (best for pixel art)
- Floyd-Steinberg: Error diffusion, adds noise
- Ordered: Pattern-based, creates artifacts
- Recommendation: Disable dithering for pixel art
Optimization Techniques
Frame Disposal Methods:
- Restore to Background: Clear before next frame
- Do Not Dispose: Keep previous frame
- Restore to Previous: Revert to prior state
Delta Frame Encoding:
- Only encode changed pixels between frames
- Significant size reduction for small changes
- Automatic in most GIF encoders
- Example: Idle animation (80% size reduction)
Transparency Optimization:
- Designate one color as transparent
- Reduce file size by not encoding unchanged areas
- Works with delta encoding
- Choose unused color for transparency index
Size Reduction Checklist:
- Use global palette
- Enable delta encoding
- Minimize frame dimensions (crop to animation bounds)
- Reduce frame count if possible
- Lower frame rate for slower animations
File Size Considerations
Size Factors:
Small (1-50 KB): 16×16 sprite, 4-8 frames, simple movement
Medium (50-200 KB): 32×32 sprite, 10-20 frames, detailed animation
Large (200KB-1MB): 64×64+ sprite, 30+ frames, complex animation
Optimization Trade-offs:
- Frame count vs. smoothness
- Canvas size vs. detail
- Frame rate vs. file size
- Color count vs. palette accuracy
Spritesheet Layouts
Spritesheets combine multiple frames or sprites into a single image for efficient loading and rendering.
Horizontal Strip
All frames arranged in a single horizontal row.
+----+----+----+----+----+
| F1 | F2 | F3 | F4 | F5 |
+----+----+----+----+----+
Use Cases:
- Simple animations (walk, run, jump)
- Scrolling backgrounds
- UI button states
Advantages:
- Simple to parse (X offset only)
- Easy to calculate frame position:
frame_x = frame_index * frame_width - Good for texture atlases with power-of-2 heights
Disadvantages:
- Very wide images with many frames
- Inefficient for non-power-of-2 widths
- GPU texture size limits (typically 4096-8192px)
Vertical Strip
All frames arranged in a single vertical column.
+----+
| F1 |
+----+
| F2 |
+----+
| F3 |
+----+
| F4 |
+----+
Use Cases:
- Mobile-optimized layouts
- Vertical scrolling effects
- Legacy systems preferring tall textures
Advantages:
- Simple to parse (Y offset only)
- Frame position:
frame_y = frame_index * frame_height - Better for portrait-oriented displays
Disadvantages:
- Very tall images
- Less common than horizontal
- Same texture size constraints
Grid Layout
Frames arranged in rows and columns.
+----+----+----+----+
| F1 | F2 | F3 | F4 |
+----+----+----+----+
| F5 | F6 | F7 | F8 |
+----+----+----+----+
| F9 | 10 | 11 | 12 |
+----+----+----+----+
Configuration:
- Rows: Number of vertical divisions
- Columns: Number of horizontal divisions
- Example: 4 columns × 3 rows = 12 frames
Frame Calculation:
frame_x = (frame_index % columns) * frame_width
frame_y = (frame_index // columns) * frame_height
Use Cases:
- Large animation sets (16+ frames)
- Multiple animations in one sheet
- Power-of-2 texture optimization
Advantages:
- Balanced dimensions (square or near-square)
- GPU-friendly (easier to fit power-of-2)
- Compact representation
Best Practices:
- Use power-of-2 dimensions: 256×256, 512×512, 1024×1024
- Add padding between frames (1-2px) to prevent bleeding
- Organize by animation (row per animation type)
Packed Layout
Optimal space usage with variable frame positions.
+--------+----+----+
| | F3 | F4 |
| F1 +----+----+
| | F5 | F6 |
+----+---+----+----+
| F2 | F7 | F8|
+----+---------+---+
Characteristics:
- Frames positioned to minimize whitespace
- Requires metadata for frame positions
- Non-uniform layout
Algorithms:
- Maxrects: Maximum rectangles bin packing
- Skyline: Skyline bottom-left heuristic
- Guillotine: Recursive subdivision
Use Cases:
- Variable-size sprites (UI elements, items)
- Texture atlases with many assets
- Maximizing GPU texture memory
Advantages:
- Smallest total texture size
- Efficient memory usage
- Professional game asset pipelines
Disadvantages:
- Requires JSON/XML metadata
- More complex to parse
- Not suitable for uniform animations
Metadata Example:
{
"frame1": {"x": 0, "y": 0, "w": 32, "h": 64},
"frame2": {"x": 0, "y": 64, "w": 16, "h": 16},
"frame3": {"x": 32, "y": 0, "w": 16, "h": 16}
}
JSON Metadata Formats
JSON files provide frame data, animation metadata, and asset information for game engines.
Aseprite JSON Format
Native format exported by Aseprite with comprehensive metadata.
{
"frames": {
"sprite_0.aseprite": {
"frame": {"x": 0, "y": 0, "w": 32, "h": 32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x": 0, "y": 0, "w": 32, "h": 32},
"sourceSize": {"w": 32, "h": 32},
"duration": 100
},
"sprite_1.aseprite": {
"frame": {"x": 32, "y": 0, "w": 32, "h": 32},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x": 0, "y": 0, "w": 32, "h": 32},
"sourceSize": {"w": 32, "h": 32},
"duration": 100
}
},
"meta": {
"app": "http://www.aseprite.org/",
"version": "1.3.0",
"image": "sprite.png",
"format": "RGBA8888",
"size": {"w": 128, "h": 32},
"scale": "1",
"frameTags": [
{
"name": "walk",
"from": 0,
"to": 3,
"direction": "forward"
}
],
"layers": [
{"name": "Layer 1", "opacity": 255, "blendMode": "normal"}
],
"slices": []
}
}
Key Fields:
frames: Per-frame data with positions and durationsframeTags: Animation sequences (walk, run, idle)direction: forward, reverse, pingpongduration: Frame duration in milliseconds
TexturePacker JSON Format
Industry-standard format used by many tools and engines.
{
"frames": {
"hero_walk_00.png": {
"frame": {"x": 2, "y": 2, "w": 32, "h": 32},
"rotated": false,
"trimmed": true,
"spriteSourceSize": {"x": 2, "y": 1, "w": 32, "h": 32},
"sourceSize": {"w": 36, "h": 34},
"pivot": {"x": 0.5, "y": 0.5}
}
},
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "spritesheet.png",
"format": "RGBA8888",
"size": {"w": 512, "h": 512},
"scale": "1"
}
}
Key Features:
trimmed: Indicates if transparent pixels were removedspriteSourceSize: Position of trimmed sprite in originalpivot: Rotation/scaling origin point (0-1 normalized)- Widely supported by game frameworks
Unity Sprite Atlas Format
Unity-specific JSON structure for sprite atlases.
{
"name": "PlayerAtlas",
"spriteSheet": {
"sprites": [
{
"name": "player_idle_0",
"rect": {"x": 0, "y": 0, "width": 32, "height": 32},
"pivot": {"x": 0.5, "y": 0},
"border": {"x": 0, "y": 0, "z": 0, "w": 0},
"alignment": 7,
"pixelsPerUnit": 32
},
{
"name": "player_idle_1",
"rect": {"x": 32, "y": 0, "width": 32, "height": 32},
"pivot": {"x": 0.5, "y": 0},
"border": {"x": 0, "y": 0, "z": 0, "w": 0},
"alignment": 7,
"pixelsPerUnit": 32
}
]
}
}
Unity-Specific Fields:
pixelsPerUnit: Sprite resolution (typically 32 or 100)alignment: Pivot preset (7 = bottom-center)border: 9-slice borders for UI scaling- Compatible with Unity's SpriteAtlas system
Godot SpriteFrames Format
Godot engine's resource format for AnimatedSprite nodes.
{
"frames": [
{
"name": "walk",
"frames": [
{"texture": "res://sprites/player_walk_0.png", "duration": 0.1},
{"texture": "res://sprites/player_walk_1.png", "duration": 0.1},
{"texture": "res://sprites/player_walk_2.png", "duration": 0.1},
{"texture": "res://sprites/player_walk_3.png", "duration": 0.1}
]
},
{
"name": "idle",
"frames": [
{"texture": "res://sprites/player_idle_0.png", "duration": 0.5},
{"texture": "res://sprites/player_idle_1.png", "duration": 0.5}
]
}
],
"speed": 10.0,
"loop": true
}
Godot Features:
- Animation-based organization (not single frames)
- Per-frame duration in seconds
- Global speed multiplier
- Directly compatible with AnimatedSprite node
Scaling Considerations
Pixel art requires special handling when scaling to maintain crisp, pixel-perfect appearance.
Nearest-Neighbor Algorithm
How It Works:
- Each output pixel uses value from closest input pixel
- No interpolation or blending
- Preserves hard edges and exact colors
Mathematical Definition:
output[x, y] = input[floor(x / scale), floor(y / scale)]
Results:
- Perfectly sharp pixels
- No color bleeding or artifacts
- Integer scales produce perfect results
- Non-integer scales may show uneven pixels
Essential for Pixel Art:
- Maintains artistic intent
- Preserves hand-placed pixels
- No blur or antialiasing
- Industry standard for retro aesthetics
Why Other Algorithms Blur Pixels
Bilinear Interpolation:
- Averages 4 nearest pixels
- Creates smooth gradients
- Problem: Blurs pixel art edges
- Use case: Photography, 3D textures
Bicubic Interpolation:
- Averages 16 nearest pixels
- Even smoother than bilinear
- Problem: Severe blurring for pixel art
- Use case: High-quality photo scaling
Lanczos Resampling:
- Advanced algorithm, high quality
- Problem: Introduces ringing artifacts
- Use case: Photographic downscaling
Visual Comparison:
Original (8×8): Nearest (16×16): Bilinear (16×16):
████████ ████████████████ ░░░░░░░░░░░░░░░░
██ ██ ████ ████ ░░██ ██░░
██ ██ ------> ████ ████ ░░██ ██░░
████████ ████████████████ ░░░░░░░░░░░░░░░░
(Sharp edges) (Sharp edges) (Blurred edges)
Common Scale Factors
Integer Scales (Recommended):
- 1x: Native resolution (32×32 remains 32×32)
- 2x: Double size (32×32 becomes 64×64)
- 4x: Quad size (32×32 becomes 128×128)
- 8x: Octo size (32×32 becomes 256×256)
Benefits:
- Perfect pixel mapping (1 source pixel → N×N output pixels)
- No distortion or uneven pixels
- Crisp, clean appearance
Non-Integer Scales (Use with Caution):
- 1.5x: Some pixels larger than others
- 3x: Still integer, good quality
- 2.5x: Uneven pixel sizes, slight distortion
Best Practices:
- Prefer integer scales (2x, 3x, 4x)
- Design base sprites at smallest playable size
- Scale up for high-DPI displays
- Use viewport/camera scaling instead of asset scaling when possible
Maintaining Pixel-Perfect Appearance
Canvas Alignment:
- Position sprites on integer coordinates
- Avoid sub-pixel positioning (0.5, 1.3, etc.)
- Round camera position to integers
Viewport Scaling:
- Scale entire game viewport, not individual sprites
- Maintain aspect ratio
- Use integer scale factors
Export Settings:
- Disable antialiasing
- Use nearest-neighbor filter
- Set texture filtering to "Point" or "Nearest"
GPU Texture Settings:
- Mag Filter: Nearest/Point
- Min Filter: Nearest/Point (not Mipmap)
- Wrap Mode: Clamp or Repeat as needed
Game Engine Integration Examples
Unity
Import Settings:
Texture Type: Sprite (2D and UI)
Sprite Mode: Multiple (for spritesheets)
Pixels Per Unit: 32 (or native sprite size)
Filter Mode: Point (no filter)
Compression: None
Format: RGBA 32 bit
Sprite Atlas Usage:
using UnityEngine;
public class SpriteAnimator : MonoBehaviour
{
public Sprite[] walkFrames;
private SpriteRenderer spriteRenderer;
private int currentFrame = 0;
private float frameTimer = 0f;
private float frameDuration = 0.1f;
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
void Update()
{
frameTimer += Time.deltaTime;
if (frameTimer >= frameDuration)
{
frameTimer = 0f;
currentFrame = (currentFrame + 1) % walkFrames.Length;
spriteRenderer.sprite = walkFrames[currentFrame];
}
}
}
Godot
SpriteFrames Setup:
# Import spritesheet
var texture = preload("res://sprites/player.png")
# Create SpriteFrames resource
var sprite_frames = SpriteFrames.new()
# Add animation
sprite_frames.add_animation("walk")
sprite_frames.set_animation_speed("walk", 10.0)
# Add frames (assume 32×32 frames in horizontal strip)
for i in range(4):
var atlas = AtlasTexture.new()
atlas.atlas = texture
atlas.region = Rect2(i * 32, 0, 32, 32)
sprite_frames.add_frame("walk", atlas, i)
AnimatedSprite Configuration:
extends AnimatedSprite
func _ready():
# Disable filtering for pixel-perfect
texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
# Play animation
play("walk")
Project Settings:
Rendering > Textures > Canvas Textures > Default Texture Filter: Nearest
Display > Window > Stretch > Mode: viewport
Display > Window > Stretch > Aspect: keep
Phaser
Loading Spritesheet:
class GameScene extends Phaser.Scene {
preload() {
// Load spritesheet
this.load.spritesheet('player', 'assets/player.png', {
frameWidth: 32,
frameHeight: 32
});
}
create() {
// Create animation from frames 0-3
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('player', {
start: 0,
end: 3
}),
frameRate: 10,
repeat: -1
});
// Create sprite
const player = this.add.sprite(100, 100, 'player');
player.play('walk');
// Disable texture smoothing for pixel-perfect
player.setTexture('player').setOrigin(0.5, 0.5);
}
}
Game Configuration:
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
pixelArt: true, // Enables nearest-neighbor filtering
render: {
antialias: false,
pixelArt: true
},
scene: [GameScene]
};
const game = new Phaser.Game(config);
Generic Engine Patterns
Frame-Based Animation Loop:
class Animation {
constructor(frames, frameDuration) {
this.frames = frames; // Array of frame data {x, y, w, h}
this.frameDuration = frameDuration; // Milliseconds per frame
this.currentFrame = 0;
this.elapsedTime = 0;
}
update(deltaTime) {
this.elapsedTime += deltaTime;
if (this.elapsedTime >= this.frameDuration) {
this.elapsedTime = 0;
this.currentFrame = (this.currentFrame + 1) % this.frames.length;
}
}
getCurrentFrame() {
return this.frames[this.currentFrame];
}
}
Spritesheet Rendering:
function drawSprite(context, image, frame, x, y, scale) {
// Use nearest-neighbor scaling
context.imageSmoothingEnabled = false;
context.drawImage(
image,
frame.x, frame.y, frame.w, frame.h, // Source
Math.floor(x), Math.floor(y), // Destination (integer coords)
frame.w * scale, frame.h * scale // Scaled size
);
}
JSON Metadata Parsing:
async function loadSpritesheet(imagePath, jsonPath) {
const [image, metadata] = await Promise.all([
loadImage(imagePath),
fetch(jsonPath).then(r => r.json())
]);
const animations = {};
// Parse Aseprite JSON format
for (const tag of metadata.meta.frameTags) {
const frames = [];
for (let i = tag.from; i <= tag.to; i++) {
const frameKey = Object.keys(metadata.frames)[i];
const frameData = metadata.frames[frameKey];
frames.push({
x: frameData.frame.x,
y: frameData.frame.y,
w: frameData.frame.w,
h: frameData.frame.h,
duration: frameData.duration
});
}
animations[tag.name] = new Animation(frames, frames[0].duration);
}
return { image, animations };
}
Best Practices Summary:
- Always disable texture filtering (use nearest/point)
- Position sprites on integer pixel coordinates
- Use integer scale factors
- Set
imageSmoothingEnabled = falsefor canvas - Configure engine for pixel-perfect rendering
- Load metadata alongside spritesheet images
- Cache parsed animations for performance