Skip to content

Thumbnail

Fast thumbnail generation with shrink-on-load optimization for maximum performance.

Overview

The thumbnail API is optimized for generating small images from large sources. Unlike standard resize(), it uses shrink-on-load to decode images at reduced resolution, providing 4-10x faster processing for large images.

Functions

thumbnail

Generate a thumbnail with full metadata about the operation.

typescript
function thumbnail(input: Buffer, options: ThumbnailOptions): Promise<ThumbnailResult>
function thumbnailSync(input: Buffer, options: ThumbnailOptions): ThumbnailResult

Parameters

ParameterTypeDescription
inputBufferImage buffer (JPEG, PNG, WebP, etc.)
optionsThumbnailOptionsThumbnail options

Returns

ThumbnailResult object:

PropertyTypeDescription
dataBufferThumbnail image data
widthnumberOutput width
heightnumberOutput height
formatstringOutput format (jpeg, png, webp)
shrinkOnLoadUsedbooleanWhether shrink-on-load was used
originalWidthnumberOriginal image width
originalHeightnumberOriginal image height

Example

typescript
import { thumbnail } from 'imgkit';

const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());

const result = await thumbnail(buffer, {
  width: 200,
  format: 'Jpeg',
  quality: 85,
});

console.log(`${result.width}x${result.height} ${result.format}`);
console.log(`Shrink-on-load used: ${result.shrinkOnLoadUsed}`);
console.log(`Original: ${result.originalWidth}x${result.originalHeight}`);

await Bun.write('thumb.jpg', result.data);

thumbnailBuffer

Generate a thumbnail and return just the buffer (simpler API).

typescript
function thumbnailBuffer(input: Buffer, options: ThumbnailOptions): Promise<Buffer>
function thumbnailBufferSync(input: Buffer, options: ThumbnailOptions): Buffer

Parameters

Same as thumbnail().

Returns

Buffer - Thumbnail image data only.

Example

typescript
import { thumbnailBuffer } from 'imgkit';

const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());

const thumb = await thumbnailBuffer(buffer, { width: 200 });
await Bun.write('thumb.jpg', thumb);

Types

ThumbnailOptions

typescript
interface ThumbnailOptions {
  width: number;              // Target width (required)
  height?: number;            // Target height (optional, maintains aspect ratio)
  format?: ThumbnailFormat;   // Output format (Jpeg, Png, Webp)
  quality?: number;           // Quality 1-100 (default: 80, or 70 in fast mode)
  shrinkOnLoad?: boolean;     // Enable shrink-on-load (default: true)
  filter?: ResizeFilter;      // Resize filter (auto-selected based on scale)
  fastMode?: boolean;         // Enable fast mode (default: false)
}

ThumbnailFormat

typescript
type ThumbnailFormat = 'Jpeg' | 'Png' | 'Webp';

ThumbnailResult

typescript
interface ThumbnailResult {
  data: Buffer;              // Thumbnail image data
  width: number;             // Output width
  height: number;            // Output height
  format: string;            // Output format
  shrinkOnLoadUsed: boolean; // Whether shrink-on-load was used
  originalWidth: number;     // Original image width
  originalHeight: number;    // Original image height
}

Shrink-on-Load Optimization

How It Works

Standard image resize:

File → Full Decode (4000x3000) → Resize (200px) → Encode
       ↑ SLOW - 12MP pixels ↑

Shrink-on-load resize:

File → Partial Decode (500x375) → Resize (200px) → Encode
       ↑ FAST - 0.2MP pixels ↑

Format Support

FormatShrink FactorsSpeedup
JPEG1/2, 1/4, 1/84-8x
WebPAny scale2-4x
PNGN/A (lossless)1x

When It's Used

Shrink-on-load is automatically used when:

  • shrinkOnLoad: true (default)
  • Target dimensions are significantly smaller than source
  • Format supports shrink-on-load (JPEG, WebP)

Fast Mode

Enable fastMode: true for maximum speed with slight quality tradeoff:

SettingNormal ModeFast Mode
Shrink factorConservativeAggressive (1/8)
Final resizeExact dimensionsSkip if within 15%
Resize filterLanczos3Nearest neighbor
Default quality8070

Example

typescript
// Maximum speed
const thumb = await thumbnail(buffer, {
  width: 200,
  fastMode: true,
});

// May return 210x118 instead of 200x112 (within 15% tolerance)

Performance

Single Image Benchmarks

2205x1240 JPEG → 200px thumbnail:

MethodTimevs Sharp
thumbnail (shrinkOnLoad)9.1ms1.2x faster
thumbnail (fastMode)9.1ms1.2x faster
thumbnail (no shrink)16.4ms1.5x slower
resize (standard)12.4ms1.1x slower
sharp10.9msbaseline

11384x4221 JPEG (10MB) → 200px thumbnail:

MethodTimevs Sharp
thumbnail (shrinkOnLoad)100ms1.2x faster
thumbnail (fastMode)107ms1.1x faster
thumbnail (no shrink)241ms2.0x slower
sharp119msbaseline

Concurrent Processing

100 images (2205x1240 JPEG):

MethodTotalPer Imagevs Sharp
thumbnail (async)167ms1.7ms1.8x faster
fastMode (async)157ms1.6ms1.9x faster
sharp (async)295ms2.9msbaseline

Use Cases

Web Thumbnails

typescript
import { thumbnailBuffer } from 'imgkit';

async function generateThumbnail(upload: Buffer) {
  return thumbnailBuffer(upload, {
    width: 300,
    format: 'Webp',
    quality: 80,
  });
}

Multiple Sizes

typescript
import { thumbnail } from 'imgkit';

async function generateSizes(buffer: Buffer) {
  const sizes = [64, 128, 256, 512];

  return Promise.all(
    sizes.map(width =>
      thumbnail(buffer, { width, format: 'Jpeg', quality: 85 })
    )
  );
}

High-Throughput Processing

typescript
import { thumbnailBuffer } from 'imgkit';

async function processBatch(images: Buffer[]) {
  // Process all images concurrently with fast mode
  return Promise.all(
    images.map(img =>
      thumbnailBuffer(img, { width: 200, fastMode: true })
    )
  );
}

API Endpoint

typescript
import { thumbnailBuffer } from 'imgkit';

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    const width = parseInt(url.searchParams.get('w') || '200');
    const fast = url.searchParams.get('fast') === '1';

    const imageUrl = url.searchParams.get('url');
    const response = await fetch(imageUrl!);
    const buffer = Buffer.from(await response.arrayBuffer());

    const thumb = await thumbnailBuffer(buffer, {
      width,
      fastMode: fast,
      format: 'Webp',
      quality: 80,
    });

    return new Response(thumb, {
      headers: { 'Content-Type': 'image/webp' }
    });
  }
});

Comparison: thumbnail vs resize

Featurethumbnail()resize()
Shrink-on-load✅ Yes❌ No
Fast mode✅ Yes❌ No
Output formatJPEG/PNG/WebPPNG only
Quality control✅ Yes❌ No
Metadata✅ Full result❌ Buffer only
Best forThumbnails, previewsPrecise resizing

Recommendation: Use thumbnail() for generating small images from large sources. Use resize() when you need precise control or are doing small scale changes.