Perfect pixel-art scaling for your Expo apps. No blur, no artifacts - just crisp, clean pixels.
- Crisp nearest-neighbor scaling (no blurry pixels!)
- Advanced fractional scaling for non-integer scale factors
- Works with local, remote, and base64 images
- Platform-specific rendering modes (software/hardware) for quality vs performance
- Native performance
- Loading states with customizable components
- Error handling with fallback options
- Type-safe with full TypeScript support
- Zero configuration needed
Platform | Supported |
---|---|
iOS | ✅ New Architecture |
Android | ✅ New Architecture |
Web | ❌ |
Note: This module requires the New Architecture (Fabric) to be enabled in your Expo project. It will not work with the old architecture.
Web Platform: This module uses native implementations for pixel-perfect scaling and does not support web platforms. For web-specific pixel art needs, consider using CSS solutions or a web-specific library.
npx expo install expo-pixel-perfect
import ExpoPixelPerfectView from 'expo-pixel-perfect';
export default function Game() {
return (
<ExpoPixelPerfectView
source={require('./assets/sprite.png')}
scale={4}
/>
);
}
import ExpoPixelPerfectView from 'expo-pixel-perfect';
export default function Game() {
return (
<ExpoPixelPerfectView
// Source image
source={require('./assets/sprite.png')}
// Scale to specific width
scale={{ targetWidth: 64 }}
// Use optimized algorithm for non-integer scaling
scaleMode="fractional-optimized"
// Platform-specific rendering modes
android_renderMode="software" // Higher quality on Android
ios_renderMode="software" // Higher quality on iOS
// Custom loading component
loadingComponent={<CustomLoader />}
// Error handling
fallback={<Text>Failed to load sprite</Text>}
onError={(error) => console.error('Failed to load:', error)}
// Load callback
onLoad={() => console.log('Sprite loaded successfully')}
// Standard React Native styles
style={styles.sprite}
/>
);
}
import ExpoPixelPerfectView from 'expo-pixel-perfect';
export default function Game() {
// Base64 encoded pixel art
const pixelArtBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...';
return (
<ExpoPixelPerfectView
source={{
base64: pixelArtBase64,
width: 16, // Original width is required
height: 16 // Original height is required
}}
scale={3}
/>
);
}
import ExpoPixelPerfectView from 'expo-pixel-perfect';
import { useWindowDimensions } from 'react-native';
export default function ResponsiveGameElement() {
const { width } = useWindowDimensions();
const spriteOriginalWidth = 32;
// Calculate a non-integer scale factor to fit screen width
const scaleFactor = width / spriteOriginalWidth / 2; // Fit 2 sprites side by side
return (
<ExpoPixelPerfectView
source={require('./assets/game-element.png')}
scale={scaleFactor}
scaleMode="fractional-optimized" // Use optimized scaling for non-integer scales
/>
);
}
Prop | Type | Description |
---|---|---|
source | number | { uri: string } | { base64: string, width: number, height: number } | Image source (local, remote, or base64) |
Prop | Type | Default | Description |
---|---|---|---|
scale | number | { targetWidth: number } | { targetHeight: number } | 1 | Scaling factor or target dimensions |
style | ViewStyle | undefined | Standard React Native view styles |
loadingComponent | ReactNode | null | Component shown during loading |
fallback | ReactNode | null | Component shown on error |
onError | (error: Error) => void | undefined | Error callback |
onLoad | () => void | undefined | Success callback |
scaleMode | "nearest" | "fractional-optimized" | "nearest" | Algorithm used for scaling |
android_renderMode | "software" | "hardware" | "hardware" | Android-specific rendering mode |
ios_renderMode | "software" | "hardware" | "hardware" | iOS-specific rendering mode |
This module provides platform-specific rendering modes to balance between visual quality and performance:
- software: Uses CPU-based rendering (View.LAYER_TYPE_SOFTWARE) for highest quality pixel-perfect rendering. Best for static UI elements or when visual quality is critical.
- hardware: Uses GPU-accelerated rendering (View.LAYER_TYPE_HARDWARE) for better performance. May have slightly less precise scaling but offers better battery life and performance.
- software: Uses CoreGraphics (CPU-based) rendering with precise nearest-neighbor scaling. Best for static UI elements or when visual quality is critical.
- hardware: Uses Core Image (GPU-accelerated) rendering for better performance. May have slightly less precise scaling but offers better battery life and performance.
// Example of platform-specific optimization
<PixelImage
source={require('./assets/character.png')}
scale={4}
// Use software rendering on Android for best quality
android_renderMode="software"
// Use hardware acceleration on iOS for better performance
ios_renderMode="hardware"
/>
This module offers two different scaling algorithms to handle different types of pixel art scaling:
- Default scaling mode
- Best for integer scaling (2x, 3x, 4x, etc.)
- Fast and efficient
- May appear slightly blurry with non-integer scaling factors
- Advanced scaling algorithm designed for non-integer scaling factors
- Uses a multi-step scaling process for sharper results
- Perfect for when you need to scale by factors like 1.5x, 2.3x, etc.
- Especially useful for responsive UIs or fitting to screen dimensions
- Uses more resources but produces significantly sharper results for fractional scales
// Responsive UI with sharp pixel art
<PixelImage
source={require('./assets/ui-element.png')}
scale={deviceWidth / spriteWidth} // Non-integer scale to fit screen
scaleMode="fractional-optimized"
/>
- Start with small source images (8x8, 16x16, 32x32)
- Use PNG format for transparency support
- For fixed sizes, use integer scales (2x, 3x) with "nearest" mode for best performance
- For responsive UI, use "fractional-optimized" mode with calculated scale factors
- Provide fallback components for better user experience
- Handle loading and error states for smoother UX
- Use "software" rendering mode for static UI elements for best quality
- Use "hardware" rendering mode for animated elements for better performance
- When using base64 images, always specify the original width and height, if you can't you could render it using a native image and getting its layout
Make sure you're using the correct scale factor. For a 16x16 image to display at 64x64, use either:
scale={4}
// or
scale={{ targetWidth: 64 }}
For non-integer scaling that still looks crisp, use the fractional-optimized mode:
scale={3.2} // Non-integer scale
scaleMode="fractional-optimized"
If still blurry, try using the "software" rendering mode:
android_renderMode="software"
ios_renderMode="software"
Check that your asset path is correct and the image exists. The onError callback can help debug:
onError={(error) => console.error('Loading failed:', error)}
When using base64 images, you must specify the original width and height:
source={{
base64: myBase64String,
width: 16, // Important! Must be exact
height: 16 // Important! Must be exact
}}
// This
<PixelImage
source={require('./assets/important-button.png')}
scale={2.5}
scaleMode="fractional-optimized"
/>
// This will be slightly blurry
<PixelImage
source={require('./assets/background-element.png')}
scale={2.5}
scaleMode="nearest"
/>
Made for ⚔️ Pixel Odyssey by Lino Iten