Skip to main content

ImageDecoder

The ImageDecoder class decodes compressed image data (JPEG, PNG, WebP, GIF, AVIF) into VideoFrame objects that can be processed, edited, or re-encoded.
This implementation follows the W3C WebCodecs ImageDecoder specification.

Quick Example

Decode a JPEG or PNG image into a VideoFrame:
import { ImageDecoder } from 'node-webcodecs';
import { readFileSync } from 'fs';

// Read image file
const imageData = readFileSync('photo.jpg');

// Create decoder
const decoder = new ImageDecoder({
  data: imageData,
  type: 'image/jpeg',
});

// Wait for image to be fully parsed
await decoder.completed;

// Decode the image
const result = await decoder.decode();
const frame = result.image;

console.log(`Decoded ${frame.codedWidth}x${frame.codedHeight} image`);

// Process the frame...

// IMPORTANT: Always close frames when done
frame.close();
decoder.close();
Always call frame.close() on decoded imagesDecoded VideoFrame objects hold native memory that is not tracked by JavaScript’s garbage collector. Forgetting to close frames will cause memory leaks.
// Good
const result = await decoder.decode();
processFrame(result.image);
result.image.close();  // Release memory

// Bad - memory leak!
const result = await decoder.decode();
processFrame(result.image);
// Forgot to close!

Supported Formats

FormatMIME TypeAnimated
JPEGimage/jpegNo
PNGimage/pngNo
WebPimage/webpYes
GIFimage/gifYes
AVIFimage/avifYes
Use ImageDecoder.isTypeSupported() to check format support at runtime.

Constructor

new ImageDecoder(init: ImageDecoderInit): ImageDecoder
Creates a new ImageDecoder instance.

Parameters

init
ImageDecoderInit
required
Configuration object for the decoder.

Example

import { ImageDecoder } from 'node-webcodecs';

// From Buffer
const decoder = new ImageDecoder({
  data: imageBuffer,
  type: 'image/png',
});

// With scaling
const thumbnailDecoder = new ImageDecoder({
  data: imageBuffer,
  type: 'image/jpeg',
  desiredWidth: 200,
  desiredHeight: 200,
});

// From ReadableStream (progressive loading)
const streamDecoder = new ImageDecoder({
  data: readableStream,
  type: 'image/webp',
});

Accessors

complete
boolean
Whether all image data has been received and parsed.For images loaded from a Buffer, this is true immediately after construction. For images loaded from a ReadableStream, this becomes true when the stream ends.
completed
Promise<void>
A promise that resolves when complete becomes true.Use this to wait for the image to be fully loaded before decoding:
const decoder = new ImageDecoder({ data: stream, type: 'image/gif' });
await decoder.completed;
console.log('Image fully loaded, ready to decode');
type
string
The MIME type of the image being decoded.
console.log(decoder.type); // 'image/jpeg'

Static Methods

isTypeSupported()

static isTypeSupported(type: string): Promise<boolean>
Check if a MIME type is supported for decoding.
import { ImageDecoder } from 'node-webcodecs';

async function checkSupport() {
  const formats = ['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/gif'];

  for (const format of formats) {
    const supported = await ImageDecoder.isTypeSupported(format);
    console.log(`${format}: ${supported ? 'Supported' : 'Not supported'}`);
  }
}

checkSupport();
// Output:
// image/jpeg: Supported
// image/png: Supported
// image/webp: Supported
// image/avif: Supported
// image/gif: Supported
Parameters:
  • type (string): The MIME type to check
Returns: Promise<boolean> - Resolves to true if the format is supported

Instance Methods

decode()

decode(options?: ImageDecodeOptions): Promise<ImageDecodeResult>
Decode an image frame.
const decoder = new ImageDecoder({
  data: imageBuffer,
  type: 'image/jpeg',
});

const result = await decoder.decode();

console.log(`Decoded: ${result.image.codedWidth}x${result.image.codedHeight}`);
console.log(`Complete: ${result.complete}`);

result.image.close();
import { ImageDecoder } from 'node-webcodecs';
import { readFileSync } from 'fs';

async function extractGifFrames(gifPath: string) {
  const gifData = readFileSync(gifPath);

  const decoder = new ImageDecoder({
    data: gifData,
    type: 'image/gif',
  });

  await decoder.completed;

  const frames = [];
  let frameIndex = 0;

  while (true) {
    try {
      const result = await decoder.decode({ frameIndex });

      // Clone the frame if you need to keep it after decoding more frames
      const frameClone = result.image.clone();
      frames.push(frameClone);

      // Close the original
      result.image.close();

      console.log(`Extracted frame ${frameIndex}: ${frameClone.codedWidth}x${frameClone.codedHeight}`);

      // Check if this was the last frame
      if (result.complete) {
        break;
      }

      frameIndex++;
    } catch (error) {
      // No more frames
      break;
    }
  }

  console.log(`Extracted ${frames.length} frames from GIF`);

  // Process frames...

  // Clean up
  frames.forEach(frame => frame.close());
  decoder.close();

  return frames.length;
}

extractGifFrames('animation.gif');
import { ImageDecoder, VideoEncoder, VideoFrame } from 'node-webcodecs';

async function convertWebpToVideo(webpPath: string, outputPath: string) {
  const webpData = readFileSync(webpPath);

  const decoder = new ImageDecoder({
    data: webpData,
    type: 'image/webp',
    preferAnimation: true,
  });

  await decoder.completed;

  // Get first frame to determine dimensions
  const firstResult = await decoder.decode({ frameIndex: 0 });
  const { codedWidth, codedHeight } = firstResult.image;

  const chunks: Buffer[] = [];
  const encoder = new VideoEncoder({
    output: (chunk) => {
      const buffer = Buffer.alloc(chunk.byteLength);
      chunk.copyTo(buffer);
      chunks.push(buffer);
    },
    error: console.error,
  });

  encoder.configure({
    codec: 'avc1.42E01E',
    width: codedWidth,
    height: codedHeight,
    bitrate: 2_000_000,
  });

  // Encode first frame
  encoder.encode(firstResult.image, { keyFrame: true });
  firstResult.image.close();

  // Encode remaining frames
  let frameIndex = 1;
  while (true) {
    try {
      const result = await decoder.decode({ frameIndex });
      encoder.encode(result.image);
      result.image.close();

      if (result.complete) break;
      frameIndex++;
    } catch {
      break;
    }
  }

  await encoder.flush();
  encoder.close();
  decoder.close();

  writeFileSync(outputPath, Buffer.concat(chunks));
}
Parameters:
options
ImageDecodeOptions
Optional decode options.
Returns: Promise<ImageDecodeResult> - Resolves to an object containing the decoded frame

reset()

reset(): void
Reset the decoder state. Aborts any pending decode operations.
const decoder = new ImageDecoder({
  data: imageBuffer,
  type: 'image/png',
});

// Decode first frame
const result1 = await decoder.decode();
result1.image.close();

// Reset decoder state
decoder.reset();

// Decode again
const result2 = await decoder.decode();
result2.image.close();

decoder.close();
Returns: void

close()

close(): void
Close the decoder and release all resources. The decoder cannot be used after calling this method.
const decoder = new ImageDecoder({
  data: imageBuffer,
  type: 'image/jpeg',
});

try {
  const result = await decoder.decode();
  // Process frame...
  result.image.close();
} finally {
  // Always close the decoder
  decoder.close();
}
Returns: void

Interfaces

ImageDecodeResult

The result of a decode operation.
image
VideoFrame
required
The decoded image as a VideoFrame. You must call close() on this frame when done to release memory.
complete
boolean
required
Whether the image is fully decoded. For animated images, this indicates whether this is the last frame.

ImageDecodeOptions

Options for the decode() method.
frameIndex
number
The index of the frame to decode (0-based). Defaults to 0.
completeFramesOnly
boolean
Whether to only return complete frames. Defaults to true.

ImageDecoderInit

Configuration for creating an ImageDecoder.
data
BufferSource | ReadableStream<BufferSource>
required
The compressed image data.
type
string
required
The MIME type of the image.
colorSpaceConversion
'default' | 'none'
Color space conversion mode.
desiredWidth
number
Desired output width for scaling.
desiredHeight
number
Desired output height for scaling.
preferAnimation
boolean
Whether to prefer animated representation.

See Also