expo-pixel-perfect
TypeScript icon, indicating that this package has built-in type declarations

1.0.18 • Public • Published

🔍 expo-pixel-perfect

Perfect pixel-art scaling for your Expo apps. No blur, no artifacts - just crisp, clean pixels.

Demo showing 16x16 pixel art scaled up with and without expo-pixel-perfect

✨ Features

  • 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

🛠️ Supported Platforms

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.

📦 Installation

npx expo install expo-pixel-perfect

🚀 Usage

Basic Usage

import ExpoPixelPerfectView from 'expo-pixel-perfect';

export default function Game() {
    return (
        <ExpoPixelPerfectView
            source={require('./assets/sprite.png')}
            scale={4}
        />
    );
}

Advanced Usage

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}
        />
    );
}

Using Base64 Images

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}
        />
    );
}

Responsive Game UI

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
        />
    );
}

🎯 Props

Required Props

Prop Type Description
source number | { uri: string } | { base64: string, width: number, height: number } Image source (local, remote, or base64)

Optional Props

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

🖥️ Render Modes

This module provides platform-specific rendering modes to balance between visual quality and performance:

Android Render Modes

  • 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.

iOS Render Modes

  • 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"
/>

📏 Scaling Modes

This module offers two different scaling algorithms to handle different types of pixel art scaling:

Nearest Neighbor (scaleMode="nearest")

  • Default scaling mode
  • Best for integer scaling (2x, 3x, 4x, etc.)
  • Fast and efficient
  • May appear slightly blurry with non-integer scaling factors

Fractional-Optimized (scaleMode="fractional-optimized")

  • 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"
/>

🎨 Tips

  • 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

🤔 Common Issues

Image appears blurry

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"

Image not loading

Check that your asset path is correct and the image exists. The onError callback can help debug:

onError={(error) => console.error('Loading failed:', error)}

Base64 image has wrong dimensions

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
}}

Performance issues with fractional-optimized mode

// 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

Package Sidebar

Install

npm i expo-pixel-perfect

Weekly Downloads

37

Version

1.0.18

License

MIT

Unpacked Size

295 kB

Total Files

39

Last publish

Collaborators

  • linoiten