processing images

import sharp from 'sharp';

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "processing images" with this command: npx skills add doanchienthangdev/omgkit/doanchienthangdev-omgkit-processing-images

Processing Images

Quick Start

import sharp from 'sharp';

// Resize and optimize for web async function optimizeImage(inputPath: string, outputPath: string): Promise<void> { await sharp(inputPath) .resize(1200, 1200, { fit: 'inside', withoutEnlargement: true }) .webp({ quality: 80 }) .toFile(outputPath); }

// Generate thumbnail with smart crop async function generateThumbnail(inputPath: string, outputPath: string): Promise<void> { await sharp(inputPath) .resize(300, 300, { fit: 'cover', position: sharp.strategy.attention }) .jpeg({ quality: 85 }) .toFile(outputPath); }

// Convert to multiple formats async function convertFormats(inputPath: string, outputDir: string): Promise<void> { const baseName = path.basename(inputPath, path.extname(inputPath)); await Promise.all([ sharp(inputPath).webp({ quality: 80 }).toFile(${outputDir}/${baseName}.webp), sharp(inputPath).avif({ quality: 70 }).toFile(${outputDir}/${baseName}.avif), sharp(inputPath).jpeg({ quality: 85, mozjpeg: true }).toFile(${outputDir}/${baseName}.jpg), ]); }

Features

Feature Description Guide

Resizing Scale images with various fit modes Use resize() with cover, contain, fill, inside, outside

Format Conversion Convert between JPEG, PNG, WebP, AVIF Use toFormat() or format-specific methods

Optimization Reduce file size while preserving quality Set quality levels and use mozjpeg/effort options

Smart Cropping Auto-detect focal points for cropping Use sharp.strategy.attention for smart positioning

Effects Apply blur, sharpen, grayscale, tint Use blur(), sharpen(), grayscale(), tint()

Watermarks Add text or image overlays Use composite() with SVG or image buffers

Metadata Read EXIF data and image dimensions Use metadata() for width, height, format info

Color Analysis Extract dominant colors Use raw() output with color quantization

LQIP Generation Create low-quality image placeholders Resize to ~20px with blur for base64 preview

Batch Processing Process multiple images concurrently Use p-queue with controlled concurrency

Common Patterns

Responsive Image Set Generation

async function generateResponsiveSet( inputPath: string, outputDir: string, widths: number[] = [320, 640, 1024, 1920] ): Promise<{ srcset: string; sizes: string }> { const baseName = path.basename(inputPath, path.extname(inputPath)); const srcsetParts: string[] = [];

for (const width of widths) { const filename = ${baseName}-${width}w.webp; await sharp(inputPath) .resize(width, null, { withoutEnlargement: true }) .webp({ quality: 80 }) .toFile(path.join(outputDir, filename)); srcsetParts.push(${filename} ${width}w); }

return { srcset: srcsetParts.join(', '), sizes: '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw', }; }

E-commerce Product Image Processing

async function processProductImage(inputPath: string, productId: string): Promise<ProductImages> { const outputDir = path.join(MEDIA_DIR, 'products', productId); await fs.mkdir(outputDir, { recursive: true });

const sizes = [ { name: 'thumb', width: 150, height: 150 }, { name: 'small', width: 300, height: 300 }, { name: 'medium', width: 600, height: 600 }, { name: 'large', width: 1200, height: 1200 }, ];

const images: Record<string, string> = {}; for (const size of sizes) { const outputPath = path.join(outputDir, ${size.name}.webp); await sharp(inputPath) .resize(size.width, size.height, { fit: 'contain', background: '#ffffff' }) .webp({ quality: 85 }) .toFile(outputPath); images[size.name] = /media/products/${productId}/${size.name}.webp; }

// Generate LQIP placeholder const lqipBuffer = await sharp(inputPath).resize(20).blur(5).jpeg({ quality: 20 }).toBuffer(); const lqip = data:image/jpeg;base64,${lqipBuffer.toString('base64')};

return { images, lqip }; }

Image Watermarking

async function addWatermark(inputPath: string, outputPath: string, watermarkPath: string): Promise<void> { const metadata = await sharp(inputPath).metadata(); const watermark = await sharp(watermarkPath) .resize(Math.round((metadata.width || 800) * 0.2)) .toBuffer();

await sharp(inputPath) .composite([{ input: watermark, gravity: 'southeast', blend: 'over' }]) .toFile(outputPath); }

async function addTextWatermark(inputPath: string, outputPath: string, text: string): Promise<void> { const metadata = await sharp(inputPath).metadata(); const { width = 800, height = 600 } = metadata;

const svg = &#x3C;svg width="${width}" height="${height}"> &#x3C;text x="${width - 20}" y="${height - 20}" text-anchor="end" font-size="24" fill="white" opacity="0.5">${text}&#x3C;/text> &#x3C;/svg>;

await sharp(inputPath) .composite([{ input: Buffer.from(svg), gravity: 'southeast' }]) .toFile(outputPath); }

Batch Processing with Progress

import PQueue from 'p-queue';

async function batchProcessImages( inputPaths: string[], outputDir: string, transform: (image: sharp.Sharp) => sharp.Sharp, onProgress?: (completed: number, total: number) => void ): Promise<Map<string, { success: boolean; error?: string }>> { const queue = new PQueue({ concurrency: 4 }); const results = new Map<string, { success: boolean; error?: string }>(); let completed = 0;

for (const inputPath of inputPaths) { queue.add(async () => { const filename = path.basename(inputPath, path.extname(inputPath)) + '.webp'; try { let image = sharp(inputPath); image = transform(image); await image.toFile(path.join(outputDir, filename)); results.set(inputPath, { success: true }); } catch (error) { results.set(inputPath, { success: false, error: error.message }); } completed++; onProgress?.(completed, inputPaths.length); }); }

await queue.onIdle(); return results; }

Best Practices

Do Avoid

Use WebP/AVIF for modern browsers with JPEG fallback Serving only JPEG/PNG to all browsers

Generate LQIP placeholders for lazy loading Loading full images without placeholders

Cache processed images to avoid reprocessing Re-processing the same image on each request

Use withoutEnlargement to prevent upscaling Scaling images larger than their original size

Strip EXIF metadata for privacy and smaller files Exposing GPS and camera data in public images

Validate image dimensions and format before processing Processing arbitrary files without validation

Use streams for large images to reduce memory Loading very large images entirely into memory

Set appropriate quality (70-85) for web delivery Over-compressing (below 60) or under-compressing

Use sharp.strategy.attention for thumbnails Using center crop for all images

Provide fallback formats for older browsers Assuming all browsers support WebP/AVIF

Related Skills

  • media-processing - Video and audio processing

  • frontend-design - Image usage in UI design

References

  • Sharp Documentation

  • Web.dev Image Optimization

  • Squoosh - Format comparison tool

  • Can I Use AVIF

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

postgresql

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

building-nestjs-apis

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

docker

No summary provided by upstream source.

Repository SourceNeeds Review