Files
platform/seed/skills/sharp/SKILL.md
T
2026-02-16 19:34:35 +00:00

21 KiB

name, description
name description
sharp Process images using the sharp Node.js library. Use when the user wants to resize, convert, crop, composite, transform, or optimize images programmatically.

Sharp

API reference for sharp — a high-performance Node.js image processing library built on libvips.

Typically 4-5x faster than ImageMagick/GraphicsMagick. Supports JPEG, PNG, WebP, GIF, AVIF, TIFF, SVG, HEIF, JP2, and JXL.

Official docs: https://sharp.pixelplumbing.com Repository: https://github.com/lovell/sharp

Installation

npm install sharp

Requires Node.js ^18.17.0 or >= 20.3.0 (or Deno/Bun with Node-API v9).

Usage

Sharp uses a fluent, chainable API. Every call returns a Sharp instance.

import sharp from 'sharp';

await sharp('input.jpg')
  .resize(800, 600)
  .jpeg({ quality: 80 })
  .toFile('output.jpg');

Sharp implements stream.Duplex — it can be piped to/from.


Constructor

sharp([input], [options])
  • input (Buffer | string | Array): Image buffer, file path, array of inputs, or omit for stream input.

Options

Option Type Default Description
failOn string 'warning' 'none', 'truncated', 'error', 'warning'
limitInputPixels number | boolean 268402689 Max pixels; false to disable
unlimited boolean false Remove memory safety for JPEG/PNG/SVG/HEIF
autoOrient boolean false Auto-rotate per EXIF Orientation
sequentialRead boolean true Sequential vs random access
density number 72 DPI for vector images (1-100000)
ignoreIcc boolean false Ignore embedded ICC profile
pages number 1 Pages to extract; -1 for all
page number 0 Starting page (zero-based)
animated boolean false Read all frames (equiv. pages: -1)

Raw Input

sharp(buffer, { raw: { width: 100, height: 100, channels: 4 } })
Property Type Description
width number Pixel width
height number Pixel height
channels number 1-4
premultiplied boolean Skip premultiplication (default false)

Create New Image

sharp({ create: { width: 300, height: 200, channels: 4, background: '#ff0000' } })
Property Type Description
width number Pixel width
height number Pixel height
channels number 3 (RGB) or 4 (RGBA)
background string | Object Color (parsed by color module)
noise Object { type: 'gaussian', mean: 128, sigma: 30 }

Render Text

sharp({ text: { text: 'Hello', font: 'Arial', dpi: 150 } })
Property Type Default Description
text string -- UTF-8; supports Pango markup
font string -- Font name
fontfile string -- Absolute path to font file
width number 0 Word-wrap boundary; 0 = no wrap
height number 0 Max height
align string 'left' 'left', 'centre', 'center', 'right'
justify boolean false Text justification
dpi number 72 Render resolution
rgba boolean false RGBA for color emoji/Pango markup
spacing number 0 Line height in points
wrap string 'word' 'word', 'char', 'word-char', 'none'

Join Array

sharp([img1, img2, img3], { join: { across: 3, shim: 10 } })
Property Type Default Description
across number 1 Images per row
animated boolean false Join as animated image
shim number 0 Pixel gap between images
background string | Object -- Gap fill color
halign string 'left' 'left', 'centre', 'right'
valign string 'top' 'top', 'centre', 'bottom'

Clone

const pipeline = sharp('input.jpg');
const clone1 = pipeline.clone().resize(200).toFile('thumb.jpg');
const clone2 = pipeline.clone().resize(800).toFile('large.jpg');

Resize

.resize([width], [height], [options])
Option Type Default Description
width number -- Target width (null to auto-scale)
height number -- Target height (null to auto-scale)
fit string 'cover' 'cover', 'contain', 'fill', 'inside', 'outside'
position string 'centre' Gravity/position for cover/contain
background string | Object {r:0,g:0,b:0,alpha:1} Fill color for contain
kernel string 'lanczos3' 'nearest', 'linear', 'cubic', 'mitchell', 'lanczos2', 'lanczos3'
withoutEnlargement boolean false Don't upscale
withoutReduction boolean false Don't downscale
fastShrinkOnLoad boolean true JPEG/WebP shrink-on-load

Fit modes:

  • cover — crop to fill both dimensions
  • contain — letterbox within dimensions
  • fill — stretch to exact dimensions (ignores aspect ratio)
  • inside — fit within, no exceeding
  • outside — minimum size meeting both dimensions

Position values: top, right top, right, right bottom, bottom, left bottom, left, left top, north, northeast, east, southeast, south, southwest, west, northwest, centre/center

Strategy (cover only): entropy, attention

Only one resize per pipeline.


Operations

Rotation & Orientation

Method Description
.rotate([angle], [options]) Rotate by degrees; omit angle for EXIF auto-rotate. options.background for fill color
.autoOrient() Auto-orient from EXIF, then remove Orientation tag
.flip([flip]) Vertical mirror (default true)
.flop([flop]) Horizontal mirror (default true)

Transform

Method Description
.affine(matrix, [options]) 2x2 affine transform. Options: background, idx, idy, odx, ody, interpolator
.extend(extend) Add padding. Number for uniform, or { top, right, bottom, left, extendWith, background }. extendWith: 'background', 'copy', 'repeat', 'mirror'
.extract({ left, top, width, height }) Crop region. Can be called before and/or after resize
.trim([options]) Auto-crop to content. Options: background (default top-left pixel), threshold (default 10), lineArt

Enhancement

Method Description
.sharpen([options]) Sharpen. options.sigma (0.000001-10), .m1 (flat), .m2 (jagged), .x1, .y2, .y3
.blur([options]) No args: 3x3 box blur. options.sigma (0.3-1000) for Gaussian. Options: precision, minAmplitude
.median([size]) Median filter, default 3x3
.gamma([gamma], [gammaOut]) Gamma correction (1.0-3.0, default 2.2)
.normalise([options]) Stretch luminance. options.lower (default 1), .upper (default 99) percentiles
.clahe({ width, height, [maxSlope] }) Contrast Limited Adaptive Histogram Equalization

Morphology

Method Description
.dilate([width]) Dilation, default 1px
.erode([width]) Erosion, default 1px

Pixel Operations

Method Description
.negate([options]) Invert colors. options.alpha (default true)
.threshold([value], [options]) Binarize at threshold (0-255, default 128). options.greyscale (default true)
.boolean(operand, operator) Bitwise op with another image: 'and', 'or', 'eor'
.linear([a], [b]) Per-channel linear transform: a * pixel + b
.recomb(matrix) 3x3 or 4x4 color recombination matrix
.modulate([options]) Adjust brightness (multiply), saturation (multiply), hue (degrees), lightness (add)
.convolve(kernel) Custom convolution: { width, height, kernel, scale, offset }
.flatten([options]) Merge alpha with options.background, remove alpha
.unflatten() Add alpha; white becomes transparent (experimental)

Colour

Method Description
.tint(color) Apply tint, preserving alpha
.greyscale([bool]) Convert to 8-bit greyscale (alias: .grayscale())
.pipelineColourspace(space) Set pipeline colorspace (e.g. 'rgb16', 'lab', 'grey16')
.toColourspace(space) Set output colorspace (e.g. 'srgb', 'cmyk', 'b-w')

Channel

Method Description
.removeAlpha() Remove alpha channel
.ensureAlpha([alpha]) Add alpha if missing. alpha: 0 (transparent) to 1 (opaque, default)
.extractChannel(channel) Extract single channel: 0-3 or 'red', 'green', 'blue', 'alpha'
.joinChannel(images, [options]) Add channel(s) from other image(s)
.bandbool(op) Bitwise across all bands: 'and', 'or', 'eor'

Composite

.composite(images)

Overlay images onto the pipeline image. images is an array of objects:

Property Type Default Description
input Buffer | string -- Image data, file path, or create/text object
blend string 'over' Blend mode
gravity string 'centre' Placement gravity
top number -- Pixel offset from top (overrides gravity)
left number -- Pixel offset from left (overrides gravity)
tile boolean false Repeat overlay across image
premultiplied boolean false Skip premultiplication
density number 72 DPI for vector overlays

Blend modes: over, multiply, screen, overlay, darken, lighten, hard-light, soft-light, difference, exclusion, colour-dodge, colour-burn, add, saturate, clear, source, in, out, atop, dest, dest-over, dest-in, dest-out, dest-atop, xor

await sharp('base.png')
  .composite([{ input: 'overlay.png', gravity: 'southeast' }])
  .toFile('output.png');

Output

Write to File

await sharp('input.jpg').resize(800).toFile('output.jpg');

Format inferred from extension. Returns { format, size, width, height, channels, premultiplied }.

Write to Buffer

const buffer = await sharp('input.jpg').resize(800).toBuffer();
// or with info:
const { data, info } = await sharp('input.jpg').resize(800).toBuffer({ resolveWithObject: true });

Format Methods

JPEG

.jpeg([options])
Option Type Default Description
quality number 80 1-100
progressive boolean false Progressive JPEG
chromaSubsampling string '4:2:0' '4:2:0' or '4:4:4'
mozjpeg boolean false MozJPEG optimizations
force boolean true Force JPEG output

PNG

.png([options])
Option Type Default Description
progressive boolean false Progressive (interlace)
compressionLevel number 6 0-9
adaptiveFiltering boolean false Adaptive row filtering
palette boolean false Quantise to palette
quality number 100 Palette quality (1-100)
effort number 7 CPU effort (1-10, palette mode)
colours/colors number 256 Max palette colors (2-256)
dither number 1.0 Floyd-Steinberg dithering level
force boolean true Force PNG output

WebP

.webp([options])
Option Type Default Description
quality number 80 1-100
alphaQuality number 100 0-100
lossless boolean false Lossless compression
nearLossless boolean false Near-lossless mode
smartSubsample boolean false Smart chroma subsampling
preset string 'default' 'default', 'photo', 'picture', 'drawing', 'icon', 'text'
effort number 4 0-6
loop number 0 Animation loops (0 = infinite)
delay number | Array -- Frame delay(s) in ms
force boolean true Force WebP output

AVIF

.avif([options])
Option Type Default Description
quality number 50 1-100
lossless boolean false Lossless mode
effort number 4 0-9
chromaSubsampling string '4:4:4' Chroma subsampling
bitdepth number 8 8, 10, or 12

GIF

.gif([options])
Option Type Default Description
reuse boolean true Reuse palette
progressive boolean false Progressive (interlace)
colours/colors number 256 2-256
effort number 7 1-10
dither number 1.0 0-1
loop number 0 0 = infinite
delay number | Array -- Frame delay(s) in ms
force boolean true Force GIF output

TIFF

.tiff([options])
Option Type Default Description
quality number 80 1-100
compression string 'jpeg' 'none', 'jpeg', 'deflate', 'packbits', 'lzw', 'webp', 'zstd', 'jp2k', 'ccittfax4'
predictor string 'horizontal' 'none', 'horizontal', 'float'
pyramid boolean false Write image pyramid
tile boolean false Tiled TIFF
tileWidth number 256 Tile width
tileHeight number 256 Tile height
bitdepth number 8 1, 2, 4, or 8
force boolean true Force TIFF output

HEIF

.heif({ compression: 'hevc' })
Option Type Default Description
compression string required 'av1' or 'hevc'
quality number 50 1-100
lossless boolean false Lossless mode
effort number 4 0-9
bitdepth number 8 8, 10, or 12

Raw

.raw([options])
  • options.depth (string, default 'uchar'): 'char', 'uchar', 'short', 'ushort', 'int', 'uint', 'float', 'double'

Tile (DZI / Zoomify / IIIF)

.tile([options])
Option Type Default Description
size number 256 Tile size (1-8192)
overlap number 0 Tile overlap (0-8192)
layout string 'dz' 'dz', 'iiif', 'iiif3', 'zoomify', 'google'
container string 'fs' 'fs' or 'zip'
angle number 0 Rotation (multiple of 90)
background string | Object white Fill color

Metadata & Stats

metadata()

const meta = await sharp('input.jpg').metadata();

Returns without decoding pixels:

Property Type Description
format string 'jpeg', 'png', 'webp', 'gif', 'svg', etc.
width number Pixel width
height number Pixel height
space string Color space ('srgb', 'rgb', 'cmyk', 'b-w', etc.)
channels number Band count
depth string Pixel depth ('uchar', 'ushort', 'float', etc.)
density number DPI
chromaSubsampling string e.g. '4:2:0'
isProgressive boolean Progressive/interlaced
hasAlpha boolean Has alpha channel
hasProfile boolean Has ICC profile
orientation number EXIF orientation (1-8)
pages number Page count
size number Total bytes (Buffer/Stream input)
exif Buffer Raw EXIF
icc Buffer ICC profile
xmp Buffer XMP data

stats()

const stats = await sharp('input.jpg').stats();

Returns pixel-derived statistics:

Property Type Description
channels Array Per-channel: min, max, sum, mean, stdev, minX, minY, maxX, maxY
isOpaque boolean Fully opaque
entropy number Greyscale entropy
sharpness number Laplacian sharpness
dominant Object Dominant sRGB color

Metadata Preservation

By default, sharp strips all metadata and converts to sRGB.

Method Description
.keepMetadata() Preserve all metadata (EXIF, ICC, XMP, IPTC)
.keepExif() Preserve EXIF only
.withExif(exif) Set EXIF (replaces input). Object keyed by IFD
.withExifMerge(exif) Merge with existing EXIF
.keepIccProfile() Preserve ICC profile
.withIccProfile(icc, [options]) Set ICC: path or 'srgb', 'p3', 'cmyk'
.keepXmp() Preserve XMP
.withXmp(xmp) Set XMP (XML string)
.withMetadata([options]) Preserve most metadata. Options: orientation, density

Timeout

.timeout({ seconds: 30 })

Abort processing after N seconds. 0 = no timeout (default).


Utility (Static)

Property/Method Description
sharp.format Object with available input/output format booleans
sharp.versions Version info for sharp, libvips, dependencies
sharp.interpolators Enum: nearest, bilinear, bicubic, lbb, nohalo, vsqbs
sharp.cache([options]) Get/set cache: { memory: 50, files: 20, items: 100 }
sharp.concurrency([n]) Get/set thread count (default: CPU cores)
sharp.counters() Returns { queue, process }
sharp.simd([bool]) Enable/disable SIMD (default true)
sharp.block({ operation }) Block specific operations
sharp.unblock({ operation }) Unblock operations

Common Recipes

Resize and convert format

await sharp('input.png')
  .resize(800, 600)
  .webp({ quality: 80 })
  .toFile('output.webp');

Resize to fit within bounds (no upscale)

await sharp('input.jpg')
  .resize(1200, 800, { fit: 'inside', withoutEnlargement: true })
  .toFile('output.jpg');

Create thumbnail (cover crop)

await sharp('input.jpg')
  .resize(250, 250, { fit: 'cover', position: 'attention' })
  .toFile('thumb.jpg');

Crop region

await sharp('input.jpg')
  .extract({ left: 100, top: 50, width: 400, height: 300 })
  .toFile('cropped.jpg');

Add watermark overlay

await sharp('photo.jpg')
  .composite([{ input: 'watermark.png', gravity: 'southeast' }])
  .toFile('watermarked.jpg');

Composite text overlay

await sharp('photo.jpg')
  .composite([{
    input: { text: { text: 'Hello World', font: 'sans', dpi: 200, rgba: true } },
    gravity: 'south'
  }])
  .toFile('annotated.jpg');

Convert to greyscale

await sharp('input.jpg')
  .greyscale()
  .toFile('grey.jpg');

Blur

await sharp('input.jpg')
  .blur({ sigma: 5 })
  .toFile('blurred.jpg');

Rotate

await sharp('input.jpg')
  .rotate(90)
  .toFile('rotated.jpg');

Auto-orient from EXIF

await sharp('input.jpg')
  .autoOrient()
  .toFile('oriented.jpg');

Extend with padding

await sharp('input.png')
  .extend({ top: 20, bottom: 20, left: 20, right: 20, background: '#ffffff' })
  .toFile('padded.png');

Auto-trim whitespace

await sharp('input.png')
  .trim({ threshold: 10 })
  .toFile('trimmed.png');

Optimize JPEG for web

await sharp('input.jpg')
  .resize(1920, null, { withoutEnlargement: true })
  .jpeg({ quality: 75, mozjpeg: true, progressive: true })
  .toFile('optimized.jpg');

Generate AVIF from JPEG

await sharp('input.jpg')
  .avif({ quality: 50, effort: 4 })
  .toFile('output.avif');

Extract channel

await sharp('input.png')
  .extractChannel('red')
  .toFile('red-channel.png');

Get image metadata

const { width, height, format, space } = await sharp('input.jpg').metadata();

Buffer round-trip

const buffer = await sharp('input.jpg')
  .resize(300)
  .png()
  .toBuffer();

Create solid color image

await sharp({ create: { width: 100, height: 100, channels: 4, background: '#ff6600' } })
  .png()
  .toFile('orange.png');

Join images into grid

await sharp(['a.png', 'b.png', 'c.png', 'd.png'], { join: { across: 2 } })
  .toFile('grid.png');

Preserve metadata

await sharp('input.jpg')
  .resize(800)
  .keepMetadata()
  .toFile('output.jpg');

Animated GIF resize

await sharp('input.gif', { animated: true })
  .resize(200)
  .gif()
  .toFile('small.gif');

Multiple outputs from one input

const pipeline = sharp('input.jpg');
await Promise.all([
  pipeline.clone().resize(200).toFile('thumb.jpg'),
  pipeline.clone().resize(800).toFile('medium.jpg'),
  pipeline.clone().resize(1600).toFile('large.jpg'),
]);

Source