sharp
Process and transform images with Sharp for Node.js. Use when a user asks to resize images, convert image formats (WebP, AVIF, PNG, JPEG), compress images, crop or rotate photos, generate thumbnails, add watermarks, optimize images for web, batch process images, create responsive image variants, extract image metadata, or build image processing pipelines. Covers resizing, format conversion, compression, cropping, compositing, and metadata extraction.
What this skill does
# Sharp
## Overview
Sharp is the fastest image processing library for Node.js, built on libvips. It handles resizing, format conversion (JPEG, PNG, WebP, AVIF, TIFF, GIF), compression, cropping, rotation, compositing, and metadata extraction. Use it for thumbnail generation, responsive image variants, web optimization, watermarking, and batch processing pipelines.
## Instructions
### Step 1: Installation
```bash
npm install sharp
```
### Step 2: Resize and Convert
```javascript
// resize.js — Resize images and convert between formats
import sharp from 'sharp'
// Resize to specific dimensions
await sharp('input.jpg')
.resize(800, 600) // width, height
.toFile('output.jpg')
// Resize maintaining aspect ratio (fit within bounds)
await sharp('input.jpg')
.resize(800, 600, { fit: 'inside', withoutEnlargement: true })
.toFile('resized.jpg')
// Convert JPEG to WebP
await sharp('photo.jpg')
.webp({ quality: 80 })
.toFile('photo.webp')
// Convert to AVIF (best compression, modern browsers)
await sharp('photo.jpg')
.avif({ quality: 50 }) // AVIF quality scale differs — 50 ≈ JPEG 80
.toFile('photo.avif')
// PNG with compression
await sharp('screenshot.png')
.png({ compressionLevel: 9, palette: true }) // palette mode for smaller files
.toFile('optimized.png')
```
### Step 3: Generate Responsive Image Variants
```javascript
// responsive.js — Generate multiple sizes for srcset
import sharp from 'sharp'
const sizes = [320, 640, 960, 1280, 1920] // common breakpoints
async function generateVariants(inputPath, outputDir) {
/**
* Generate responsive image variants in WebP and JPEG fallback.
* Args:
* inputPath: Source image path
* outputDir: Directory for output variants
*/
const image = sharp(inputPath)
const metadata = await image.metadata()
const baseName = inputPath.replace(/\.[^.]+$/, '').split('/').pop()
for (const width of sizes) {
if (width > metadata.width) continue // don't upscale
// WebP variant (primary)
await sharp(inputPath)
.resize(width)
.webp({ quality: 80 })
.toFile(`${outputDir}/${baseName}-${width}w.webp`)
// JPEG fallback
await sharp(inputPath)
.resize(width)
.jpeg({ quality: 80, progressive: true })
.toFile(`${outputDir}/${baseName}-${width}w.jpg`)
}
}
```
### Step 4: Crop, Rotate, and Transform
```javascript
// transform.js — Crop, rotate, flip, and extract regions
import sharp from 'sharp'
// Center crop to square (for avatars/thumbnails)
await sharp('portrait.jpg')
.resize(300, 300, { fit: 'cover', position: 'centre' })
.toFile('avatar.jpg')
// Extract specific region
await sharp('photo.jpg')
.extract({ left: 100, top: 50, width: 500, height: 500 })
.toFile('cropped.jpg')
// Rotate (auto-rotate based on EXIF, or manual)
await sharp('photo.jpg')
.rotate() // auto-rotate from EXIF orientation
.toFile('rotated.jpg')
await sharp('photo.jpg')
.rotate(90) // explicit 90° clockwise
.toFile('rotated90.jpg')
// Flip and flop
await sharp('photo.jpg').flip().toFile('flipped.jpg') // vertical flip
await sharp('photo.jpg').flop().toFile('flopped.jpg') // horizontal mirror
```
### Step 5: Watermark and Compositing
```javascript
// watermark.js — Overlay watermark, compose multiple images
import sharp from 'sharp'
// Add watermark
await sharp('photo.jpg')
.composite([{
input: 'watermark.png',
gravity: 'southeast', // bottom-right corner
blend: 'over',
}])
.toFile('watermarked.jpg')
// Text watermark (create text as SVG, then overlay)
const svgText = `
<svg width="400" height="50">
<text x="0" y="35" font-size="30" fill="white" opacity="0.5"
font-family="Arial">© 2025 My Company</text>
</svg>`
await sharp('photo.jpg')
.composite([{
input: Buffer.from(svgText),
gravity: 'south',
}])
.toFile('branded.jpg')
// Create image collage (multiple images on a canvas)
await sharp({ create: { width: 1200, height: 600, channels: 3, background: '#ffffff' } })
.composite([
{ input: await sharp('img1.jpg').resize(400, 300).toBuffer(), left: 0, top: 0 },
{ input: await sharp('img2.jpg').resize(400, 300).toBuffer(), left: 400, top: 0 },
{ input: await sharp('img3.jpg').resize(400, 300).toBuffer(), left: 800, top: 0 },
])
.jpeg()
.toFile('collage.jpg')
```
### Step 6: Metadata and Analysis
```javascript
// metadata.js — Extract image metadata and stats
import sharp from 'sharp'
const metadata = await sharp('photo.jpg').metadata()
console.log(metadata)
// { width: 4032, height: 3024, format: 'jpeg', space: 'srgb',
// channels: 3, depth: 'uchar', density: 72, hasAlpha: false,
// orientation: 1, exif: Buffer, icc: Buffer }
// Get image statistics (min, max, mean for each channel)
const stats = await sharp('photo.jpg').stats()
console.log(stats.channels[0]) // { min: 0, max: 255, sum: 12345678, mean: 128.5, ... }
// Strip metadata (EXIF, ICC) for privacy
await sharp('photo.jpg')
.rotate() // apply EXIF rotation first
.withMetadata({ orientation: undefined }) // strip EXIF
.toFile('stripped.jpg')
```
### Step 7: Batch Processing
```javascript
// batch.js — Process all images in a directory
import sharp from 'sharp'
import { readdir } from 'fs/promises'
import path from 'path'
async function optimizeDirectory(inputDir, outputDir, maxWidth = 1920) {
/**
* Batch optimize all images: resize, convert to WebP, compress.
* Args:
* inputDir: Source directory with images
* outputDir: Destination for optimized images
* maxWidth: Maximum width (maintains aspect ratio)
*/
const files = await readdir(inputDir)
const imageFiles = files.filter(f => /\.(jpe?g|png|tiff?|webp)$/i.test(f))
let processed = 0
for (const file of imageFiles) {
const inputPath = path.join(inputDir, file)
const outputName = file.replace(/\.[^.]+$/, '.webp')
const outputPath = path.join(outputDir, outputName)
const meta = await sharp(inputPath).metadata()
const pipeline = sharp(inputPath)
if (meta.width > maxWidth) {
pipeline.resize(maxWidth)
}
await pipeline.webp({ quality: 80 }).toFile(outputPath)
processed++
}
console.log(`Optimized ${processed} images`)
}
```
## Examples
### Example 1: Build an image upload pipeline for a web app
**User prompt:** "Users upload profile photos. I need to generate a 200x200 avatar thumbnail, a 800px wide display version, and a tiny 40x40 version for comments, all in WebP."
The agent will:
1. Accept the upload buffer via `sharp(buffer)`.
2. Auto-rotate based on EXIF to handle phone photos.
3. Generate three variants with `resize()` + `webp()`, using `fit: 'cover'` for the square crops.
4. Strip EXIF metadata for privacy.
5. Return buffers or save to disk/S3.
### Example 2: Optimize a website's image assets for performance
**User prompt:** "Our marketing site has 200+ images totaling 800MB. Optimize them — convert to WebP, resize anything over 1920px, and generate AVIF variants for modern browsers."
The agent will:
1. Scan the images directory, read metadata for each file.
2. Skip already-small images, resize large ones to max 1920px width.
3. Generate WebP (quality 80) and AVIF (quality 50) variants.
4. Report total size savings and per-file breakdown.
## Guidelines
- Sharp is the fastest Node.js image library (10-100x faster than ImageMagick/GraphicsMagick via Node bindings). Prefer it for server-side image processing.
- Always call `.rotate()` before other transforms when processing user uploads — it applies EXIF orientation, preventing rotated phone photos.
- Use WebP (quality 80) as the default web format — it's 25-35% smaller than JPEG at equivalent quality and supported by all modern browsers.
- AVIF offers even better compression (50% smaller than JPEG) but encoding is slower. Use it for static assets, not real-time processing.
- Sharp operations are chainabRelated in Ads & Marketing
ads
IncludedMulti-platform paid advertising audit and optimization skill. Analyzes Google, Meta, YouTube, LinkedIn, TikTok, Microsoft, and Apple Ads. 250+ checks with scoring, parallel agents, industry templates, and AI creative generation.
banana
IncludedAI image generation Creative Director powered by Google Gemini Nano Banana models. Use this skill for ANY request involving image creation, editing, visual asset production, or creative direction. Triggers on: generate an image, create a photo, edit this picture, design a logo, make a banner, visual for my anything, and all /banana commands. Handles text-to-image, image editing, multi-turn creative sessions, batch workflows, and brand presets.
rpg-migration-analyzer
IncludedAnalyzes legacy RPG (Report Program Generator) programs from AS/400 and IBM i systems for migration to modern Java applications. Extracts business logic from RPG III/IV/ILE source code, identifies data structures (D-specs), file operations (F-specs), program dependencies (CALLB/CALLP), and converts RPG constructs to Java equivalents. Generates migration reports, complexity estimates, and Java implementation strategies with POJO classes, JPA entities, and service methods. Use when modernizing AS/400 or IBM i legacy systems, analyzing RPG source files (.rpg, .rpgle, .RPGLE), converting RPG to Java, mapping data specifications to Java classes, planning legacy system migration, or when user mentions RPG analysis, Report Program Generator, RPG III/IV/ILE, AS/400 modernization, IBM i migration, packed decimal conversion, or mainframe application rewrite.
brand-library-architect
IncludedBuild a complete brand library for a product — visual asset render pipeline, brand documentation set (BRAND, COPY, MANIFESTO, BIOS, FAQ, GLOSSARY, TONE, PRICING), open-source convention files (README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT), and a self-contained press kit. This skill should be used when the user asks to "build a brand library / brand kit / press kit / brand assets" for a product, "set up a brand library workflow," "create a positioning manifesto plus visual identity," or any combination of brand documentation + visual asset pipeline. Apply phase-by-phase or run end-to-end. Templates are product-agnostic and use {{TOKEN}} placeholders the skill prompts the user to fill.
writing-tech-post
IncludedAuthors engineering blog posts end-to-end: launch deep-dives, incident postmortems, architecture migrations, performance case studies, tutorials, AI/agent system writeups, security disclosures, and research-to-product translations. Picks the correct archetype, plans the abstraction ladder, enforces an evidence cadence (diagrams, benchmarks, profiles, traces, code, ablations), tunes voice against publisher house styles (Datadog, Vercel, GitHub, AWS, Meta, Cloudflare, Jane Street), and runs a pre-publish gate for narrative momentum and disclosure ethics. Use when drafting a new engineering post, restructuring a draft that feels flat, deciding which evidence form belongs where, validating that depth and product context are balanced, or preparing a postmortem, migration, or performance narrative for external publication. Do not use for API reference documentation, README authoring, marketing copy, release notes, generic SEO content, ghost-written executive thought leadership, or non-engineering long-form essays.
blog-google
IncludedGoogle API integration for blog performance: PageSpeed Insights, CrUX Core Web Vitals with 25-week history, Search Console performance, URL Inspection, Indexing API, GA4 organic traffic, NLP entity analysis for E-E-A-T, YouTube video search for embedding, and Google Ads Keyword Planner. Progressive feature availability based on credential tier (API key, OAuth/service account, GA4, Ads). Shares config with claude-seo at ~/.config/claude-seo/google-api.json. Use when user says "google data", "page speed", "core web vitals", "search console", "indexation", "GA4", "keyword research", "nlp entities", "blog performance", "youtube search", "google api setup".