Package Exports
- dkb-texture-optimize-pro
Readme
Texture Optimize Pro
🎨 Texture optimization library for HTML5 games (PixiJS/ThreeJS) with per-texture configuration
Optimize your game textures with Sharp - the fastest Node.js image processing library. Configure each texture individually by name with custom max sizes, formats, and quality settings.
✨ Features
- ⚡ Lightning Fast - Powered by Sharp (libvips) for 4-26x faster processing.
- 🎯 Per-Texture Configuration - Each texture can have custom settings based on filename.
- 📏 Automatic Power-of-2 - Automatically resizes to nearest power-of-2 for WebGL compatibility (32, 64, 128, 256, 512, 1024, 2048, 4096).
- 📐 Always Maintains Aspect Ratio - Never stretches or skews your images.
- 🖼️ Preserves Input Format - Your
.pngfiles stay.png, and.jpgstay.jpg. - 🗜️ Optimized Compression - Applies quality settings for PNG, JPG, and WebP.
- 📦 Batch Processing - Process entire directories with configurable concurrency.
- 🔄 Smart Re-optimization - Automatically preserves originals for unlimited quality-preserving re-optimizations.
- 🎮 Game-Optimized - Built for PixiJS, ThreeJS, and playable ads.
- 🔧 TypeScript - Full type safety and IntelliSense support.
📦 Installation
npm install dkb-texture-optimize-pro
# or
yarn add dkb-texture-optimize-pro
# or
pnpm add dkb-texture-optimize-pro🚀 Quick Start
This guide will walk you through setting up and using the texture optimizer for your project.
1. Prepare Your Project Structure
Create a directory structure for your textures:
your-project/
├── src/
│ └── assets/
│ └── textures/
│ ├── player-sprite.png
│ ├── enemy-goblin.png
│ ├── background-sky.jpg
│ └── ui/
│ ├── button.png
│ └── icon.png
└── package.json2. Initialize Configuration
Run the init command to create a sample configuration file:
npx texture-optimizer initThis creates texture-optimize-pro.json in your project root.
3. Configure Your Textures
Edit texture-optimize-pro.json to match your texture files:
{
"defaultSettings": {
"maxSize": 512,
"quality": 80
},
"textures": [
{
"name": "player-sprite",
"useDefault": false,
"maxSize": 512,
"quality": 85
},
{
"name": "enemy-goblin",
"useDefault": false,
"maxSize": 256,
"quality": 80
},
{
"name": "background-sky",
"useDefault": true
}
]
}See the "Configuration" section below for a detailed breakdown.
4. Run the Optimizer
The optimizer supports two modes:
Option A: In-Place Optimization (Recommended for Development)
Optimizes textures in their current location and backs up originals automatically:
npx texture-optimizer build --base-path src/assets/texturesWhat happens:
- Originals automatically backed up to
_originalTexture/folders - Optimized textures replace originals in place
- Perfect for iterative development and re-optimization
Expected Output:
🎨 Texture Optimizer for HTML5 Games
Config: /path/to/texture-optimize-pro.json
Input: /path/to/src/assets/textures
Mode: In-place optimization (originals → _originalTexture)
📋 Loading texture configuration from: /path/to/texture-optimize-pro.json
Found 3 configured textures in config
📁 Found 5 texture files to process
✓ [IN-PLACE] [CUSTOM] player-sprite.png → player-sprite.png (45.2% smaller, 0.15 MB, 512x512, maxSize: 512px)
✓ [IN-PLACE] [CUSTOM] enemy-goblin.png → enemy-goblin.png (52.1% smaller, 0.08 MB, 256x256, maxSize: 256px)
✓ [IN-PLACE] [DEFAULT] background-sky.jpg → background-sky.jpg (12.3% smaller, 1.24 MB, 1024x1024, maxSize: 1024px)
==================================================
Processing Summary
✓ Successful: 3/3
📦 Originals backed up to: _originalTexture/ foldersAfter optimization:
textures/
├── player-sprite.png (optimized)
├── enemy-goblin.png (optimized)
└── _originalTexture/
├── player-sprite.png (original backup)
└── enemy-goblin.png (original backup)Option B: Output Directory Mode (Recommended for Production)
Outputs optimized textures to a separate directory, leaving source untouched:
npx texture-optimizer build \
--base-path src/assets/textures \
--output dist/texturesWhat happens:
- Source files remain completely untouched
- Optimized textures written to output directory
- If
_originalTexture/exists in source, uses those as reference - Clean separation of source and build artifacts
Expected Output:
🎨 Texture Optimizer for HTML5 Games
Config: /path/to/texture-optimize-pro.json
Input: /path/to/src/assets/textures
Output: /path/to/dist/textures
📋 Loading texture configuration from: /path/to/texture-optimize-pro.json
Found 3 configured textures in config
📁 Found 5 texture files to process
✓ [CUSTOM] player-sprite.png → player-sprite.png (45.2% smaller, 0.15 MB, 512x512, maxSize: 512px)
✓ [CUSTOM] enemy-goblin.png → enemy-goblin.png (52.1% smaller, 0.08 MB, 256x256, maxSize: 256px)
✓ [DEFAULT] background-sky.jpg → background-sky.jpg (12.3% smaller, 1.24 MB, 1024x1024, maxSize: 1024px)
==================================================
Processing Summary
✓ Successful: 3/3After optimization:
src/assets/textures/ (unchanged)
├── player-sprite.png
└── enemy-goblin.png
dist/textures/ (optimized output)
├── player-sprite.png
└── enemy-goblin.png5. Re-optimize Anytime (In-Place Mode)
When using in-place mode, you can re-optimize with different settings without quality loss:
# Change settings in texture-optimize-pro.json
# (e.g., maxSize: 512 → 1024, quality: 80 → 90)
# Run optimizer again
npx texture-optimizer build --base-path src/assets/texturesThe optimizer automatically detects and uses originals from _originalTexture/:
📦 Using original from backup: player-sprite.png
✓ [IN-PLACE] [RE-OPT] [CUSTOM] player-sprite.png → player-sprite.png (72% smaller, 0.55 MB, 1024x1024, maxSize: 1024px)🔧 Configuration
Configuration File (texture-optimize-pro.json)
defaultSettings :
- Applied to all textures not listed in the textures array.
- Applied to any textures in the array with useDefault: true.
{
"defaultSettings": {
"maxSize": 512,
"quality": 80
}
}textures Array:
- A list of entries to provide custom settings for specific textures.
{
"name": "player-sprite",
"useDefault": false,
"maxSize": 512,
"quality": 85
}- name: Filename without extension (e.g., "player-sprite" matches "player-sprite.png").
- useDefault: true = use default settings, false = use custom settings specified here.
- maxSize: Maximum dimension (width or height) in pixels.
- quality: Compression quality (1-100).
Automatic Settings
- Format: The output format now always matches the input format (e.g., .png stays .png).
- Power of 2: Textures are always resized to the nearest power-of-2 dimension.
- Aspect Ratio: The texture's aspect ratio is always maintained.
Name Matching
- The
namefield matches the filename without the extension. - Example:
"name": "player-sprite"matchesplayer-sprite.png,player-sprite.jpg, etc. - The optimizer searches all subdirectories within your
--base-path.
maxSize Options
- Valid options: 32, 64, 128, 256, 512, 1024, 2048, 4096.
- The optimizer will resize to the nearest lower power-of-2, capped at this
maxSize.
Schema Reference
This is the internal schema for the settings objects:
interface TextureSettings {
maxSize?: 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;
quality?: number; // 1-100
}🔄 Re-optimization & Original Preservation
The optimizer automatically manages original textures for safe iterative optimization:
In-Place Mode
First Run:
- Optimizes textures in-place (when no
--outputspecified) - Creates
_originalTexture/backup folders automatically - Backs up original files before optimization
Subsequent Runs:
- Detects existing
_originalTexture/folders - Uses backed-up originals as source (not already-optimized files)
- Applies new settings without quality degradation
- Console shows
[RE-OPT]tag to indicate re-optimization
Benefits:
- ✨ Unlimited re-optimizations without quality loss
- 🔒 Original textures always preserved
- 🎯 Safe experimentation with different settings
- 🔄 Change maxSize or quality freely - always from original source
Example workflow:
# Initial optimization
npx texture-optimizer build -b src/textures
# Creates: src/textures/_originalTexture/
# Change settings in texture-optimize-pro.json
# (e.g., maxSize: 512 → 1024, quality: 80 → 90)
# Re-optimize with new settings
npx texture-optimizer build -b src/textures
# Uses: src/textures/_originalTexture/ as source
# Result: Fresh optimization from original, no quality loss!Output Directory Mode
With Existing Backups:
If _originalTexture/ folders exist in your source directory, the optimizer will use them as reference:
# Source has backups from previous in-place optimization
src/textures/_originalTexture/player.png (exists)
# Run with output directory
npx texture-optimizer build -b src/textures -o dist/textures
# Uses backup as source, outputs to dist/Without Backups: Uses current source files and outputs to specified directory.
Special Case - Backup-Only Folders
If you have only _originalTexture/ folders (no textures outside), the optimizer will:
- Detect textures inside
_originalTexture/ - Optimize them and output to parent directory
- Useful for restoring optimized textures from backups
# Only _originalTexture/ exists
textures/
└── _originalTexture/
└── player.png
# Run optimizer
npx texture-optimizer build -b textures
# Result:
textures/
├── player.png (optimized, created)
└── _originalTexture/
└── player.png (original, unchanged)✨ Tips & Best Practices
1. Choosing Between Modes
Use In-Place Mode when:
- Developing and iterating on optimization settings
- Want to experiment with different quality/size combinations
- Need to re-optimize frequently
- Working in development environment
Use Output Directory Mode when:
- Building for production
- Want to keep source and build separate
- Using in CI/CD pipelines
- Don't need to re-optimize frequently
2. Naming Convention
Use clear, descriptive names that match your files:
Good:
- player-idle.png → "name": "player-idle"
- enemy-boss-1.png → "name": "enemy-boss-1"
- bg-forest-day.jpg → "name": "bg-forest-day"
Bad:
- texture1.png → hard to configure
- IMG_0001.png → meaningless name3. Choose the Right Input Format
- PNG: Best for transparency, UI elements, text, or pixel art.
- JPG: Best for photos, complex backgrounds, or realistic art (no transparency).
- WebP: Best compression for modern browsers.
4. Power-of-2 for WebGL
This feature is automatic and enabled by default. It is critical for:
- ThreeJS textures (required for mipmapping).
- PixiJS textures (improves performance and compatibility).
- Any WebGL-based rendering.
5. Quality Settings
Use this as a guide for setting the quality property (1-100):
- 90-100: High-quality assets, hero characters, prominent UI.
- 80-90: Standard game assets.
- 70-80: Backgrounds, secondary elements.
- 60-70: Playable ads, or where extreme optimization is needed.
6. Test Your Settings
After optimizing, always load your game and verify:
- Textures look visually acceptable.
- No performance issues are introduced.
- File sizes are reduced.
- The total bundle size meets your requirements.
7. Managing Backups
Development:
- Keep
_originalTexture/folders for re-optimization flexibility - Commit to version control if team needs re-optimization capability
Production/Deployment:
- Exclude
_originalTexture/from builds (already larger originals) - Add to
.gitignoreif needed:echo "_originalTexture" >> .gitignore - Use output directory mode to avoid shipping backups
Disk Space:
- Backups typically use 2-3x space of optimized textures
- Trade-off for unlimited quality-preserving re-optimizations
🛠️ Integration with Your Build Process
You can run the optimizer manually or integrate it into your build scripts.
Option A: package.json Pre-build Script (In-Place)
Add to your package.json to run automatically before every build:
{
"scripts": {
"optimize-textures": "texture-optimizer build --base-path src/assets/textures",
"prebuild": "npm run optimize-textures",
"build": "vite build"
}
}Now, just run npm run build.
Option B: package.json Pre-build Script (Output Directory)
{
"scripts": {
"optimize-textures": "texture-optimizer build --base-path src/assets/textures --output dist/textures",
"prebuild": "npm run optimize-textures",
"build": "vite build"
}
}Option C: package.json Separate Commands
Keep both as separate commands to run manually:
{
"scripts": {
"optimize-inplace": "texture-optimizer build --base-path src/assets/textures",
"optimize": "texture-optimizer build --base-path src/assets/textures --output dist/textures",
"build": "vite build"
}
}Run when needed: npm run optimize-inplace or npm run optimize.
Option D: Vite Integration
You can also run the optimizer programmatically within your vite.config.ts:
import { defineConfig } from 'vite';
export default defineConfig({
// ... your config
build: {
// Run texture optimizer before build
async buildStart() {
const { BatchProcessor } = await import('dkb-texture-optimize-pro');
const processor = new BatchProcessor({
basePath: 'src/assets/textures',
outputDir: 'dist/textures', // Omit for in-place mode
textureConfigPath: 'texture-optimize-pro.json'
});
await processor.processAll();
}
}
});📖 CLI Usage
Build Command
texture-optimizer build [options]Options:
-c, --config <path>- Path to texture-optimize-pro.json (default: "texture-optimize-pro.json")-b, --base-path <path>- Base path for input textures (default: "src/assets/textures")-o, --output <path>- Output directory (if not specified, optimizes in-place and backs up originals to _originalTexture folder)--concurrency <number>- Number of concurrent operations (default: 10)--verbose- Enable verbose logging
Examples:
# In-place optimization with backup
texture-optimizer build --base-path src/assets/textures
# Output to separate directory
texture-optimizer build \
--base-path src/assets/textures \
--output dist/textures
# Process with custom config and verbose logging
texture-optimizer build \
--config my-texture-config.json \
--base-path assets/images \
--output build/optimized \
--concurrency 5 \
--verboseInit Command
texture-optimizer init [directory]Creates a sample texture-optimize-pro.json configuration file in the specified directory.
💻 Programmatic API
Single Texture Optimization
import { TextureOptimizer } from 'dkb-texture-optimize-pro';
// Settings for a single run, including the detected format
const optimizer = new TextureOptimizer({
maxSize: 512,
quality: 85,
format: 'png' // Format must be specified here
});
const result = await optimizer.optimize(
'src/player-sprite.png',
'dist/player-sprite.png' // Output matches input format
);
console.log(`Reduced by ${result.reductionPercent.toFixed(1)}%`);
console.log(`${result.originalSize.width}x${result.originalSize.height} → ${result.optimizedSize.width}x${result.optimizedSize.height}`);Batch Processing
import { BatchProcessor } from 'dkb-texture-optimize-pro';
// In-place mode (omit outputDir)
const processor = new BatchProcessor({
basePath: 'src/assets/textures',
textureConfigPath: 'texture-optimize-pro.json',
concurrency: 10,
verbose: true
});
// Output directory mode (specify outputDir)
const processorWithOutput = new BatchProcessor({
basePath: 'src/assets/textures',
outputDir: 'dist/textures',
textureConfigPath: 'texture-optimize-pro.json',
concurrency: 10,
verbose: true
});
// BatchProcessor handles format detection automatically
const results = await processor.processAll();
console.log(`Processed ${results.length} textures`);
const successful = results.filter(r => r.success);
console.log(`Success rate: ${(successful.length / results.length * 100).toFixed(1)}%`);Using Texture Config Manager
import { TextureConfigManager } from 'dkb-texture-optimize-pro';
const configManager = await TextureConfigManager.loadFromFile('texture-optimize-pro.json');
// Get settings for a specific texture
const settings = configManager.getSettingsForTexture('player-sprite.png');
console.log(`Max size: ${settings.maxSize}px`);
console.log(`Quality: ${settings.quality}`);
// Check if texture has custom settings
const hasCustom = configManager.hasCustomSettings('player-sprite.png');
console.log(`Uses custom settings: ${hasCustom}`);
// Get all configured textures
const configured = configManager.getConfiguredTextures();
console.log(`Configured textures: ${configured.join(', ')}`);🎮 Use Cases & Recommendations
For Playable Ads (5MB limit)
Aggressive optimization is key for playable ads:
{
"defaultSettings": {
"maxSize": 512,
"quality": 75
}
}Tips:
- Use maxSize 256-512 for most textures
- Quality 70-80 for good balance
- Combine with texture atlases for maximum savings
- Choose your input formats wisely (e.g., use .jpg for backgrounds, .png for UI)
- Use in-place optimization to iterate on settings until you hit size target
For PixiJS Games
Balance quality and performance:
{
"defaultSettings": {
"maxSize": 512,
"quality": 85,
},
"textures": [
{
"name": "hero-spritesheet",
"useDefault": false,
"maxSize": 2048,
"quality": 90
}
]
}For ThreeJS 3D Games
Optimize for GPU memory:
{
"defaultSettings": {
"maxSize": 1024,
"quality": 85,
},"textures": [
{
"name": "hero-diffuse-map",
"useDefault": false,
"maxSize": 1024,
"quality": 80
}
]
}Tips:
- Power-of-2 is automatic, which is critical for ThreeJS mipmapping.
- Use PNG for textures requiring alpha channel
- JPG for diffuse maps without transparency
📊 Performance
Sharp Performance Benchmarks:
- JPEG resize: 64x faster than ImageMagick
- PNG resize: 4.7x faster than ImageMagick
- WebP encoding: High quality with efficient compression
- Batch processing: 10+ images/second on typical hardware
Memory Usage:
- Configurable concurrency to manage memory
- Default: 10 concurrent operations
- Adjust based on image sizes and available RAM
🐛 Troubleshooting
Texture looks blurry Issues
Solution: The quality or maxSize is too low. Increase them in your config:
{
"name": "my-texture",
"useDefault": false,
"maxSize": 1024, // Was 512, now 1024
"quality": 90 // Was 80, now 90
}Output files too large Issues
Solution: Decrease maxSize or quality. Also, check your source asset; a large .png background might be better saved as a .jpg before optimizing.
{
"name": "my-texture",
"useDefault": false,
"maxSize": 512, // Was 1024
"quality": 75 // Was 85
}Texture not being optimized with custom settings
Solution: Check that the name in your config matches the filename exactly (without the extension).
File: player-sprite-idle.png
Config: "name": "player-sprite-idle" ✓ Correct
Config: "name": "player-sprite" ✗ Won't matchRe-optimization not working (In-Place Mode)
Solution: Ensure _originalTexture/ folders exist and contain the actual originals. Run with --verbose to see if backups are being used:
texture-optimizer build -b src/textures --verboseExpected output:
📦 Using original from backup: player-sprite.png
✓ [IN-PLACE] [RE-OPT] [CUSTOM] player-sprite.png → ...If not shown, _originalTexture/ may be missing or empty.
Sharp Installation Issues
If Sharp fails to install:
# Clear npm cache
npm cache clean --force
# Reinstall with rebuild
npm install sharp --build-from-sourceMemory Issues
For large batches, reduce concurrency:
texture-optimizer build --concurrency 5Or set environment variables:
export UV_THREADPOOL_SIZE=8
export MALLOC_ARENA_MAX=2📜 License
MIT License © Dogukan Kaan Bozkurt
🤝 Contributing
Contributions welcome! Please open an issue or PR on GitHub.