> ## Documentation Index
> Fetch the complete documentation index at: https://docs.node-webcodecs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ImageDecoder

> Decode image files into VideoFrame objects for processing

# ImageDecoder

The `ImageDecoder` class decodes compressed image data (JPEG, PNG, WebP, GIF, AVIF) into `VideoFrame` objects that can be processed, edited, or re-encoded.

<Info>
  This implementation follows the [W3C WebCodecs ImageDecoder specification](https://w3c.github.io/webcodecs/#imagedecoder-interface).
</Info>

## Quick Example

Decode a JPEG or PNG image into a VideoFrame:

```typescript theme={null}
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();
```

<Warning>
  **Always call `frame.close()` on decoded images**

  Decoded `VideoFrame` objects hold native memory that is not tracked by JavaScript's garbage collector. Forgetting to close frames will cause memory leaks.

  ```javascript theme={null}
  // 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!
  ```
</Warning>

## Supported Formats

| Format | MIME Type  | Animated |
| ------ | ---------- | -------- |
| JPEG   | image/jpeg | No       |
| PNG    | image/png  | No       |
| WebP   | image/webp | Yes      |
| GIF    | image/gif  | Yes      |
| AVIF   | image/avif | Yes      |

Use `ImageDecoder.isTypeSupported()` to check format support at runtime.

## Constructor

```typescript theme={null}
new ImageDecoder(init: ImageDecoderInit): ImageDecoder
```

Creates a new ImageDecoder instance.

### Parameters

<ParamField path="init" type="ImageDecoderInit" required>
  Configuration object for the decoder.

  <Expandable title="ImageDecoderInit properties">
    <ParamField path="data" type="BufferSource | ReadableStream<BufferSource>" required>
      The compressed image data. Can be a `Buffer`, `ArrayBuffer`, `TypedArray`, or a `ReadableStream` for progressive loading.
    </ParamField>

    <ParamField path="type" type="string" required>
      The MIME type of the image (e.g., `'image/jpeg'`, `'image/png'`, `'image/webp'`).
    </ParamField>

    <ParamField path="colorSpaceConversion" type="'default' | 'none'" default="'default'">
      How to handle color space conversion.

      * `'default'`: Convert to sRGB color space
      * `'none'`: Keep original color space
    </ParamField>

    <ParamField path="desiredWidth" type="number">
      Desired output width for scaling. The decoder may scale during decode for efficiency.
    </ParamField>

    <ParamField path="desiredHeight" type="number">
      Desired output height for scaling. The decoder may scale during decode for efficiency.
    </ParamField>

    <ParamField path="preferAnimation" type="boolean" default="true">
      For images with both animated and static representations (like APNG), whether to prefer the animated version.
    </ParamField>
  </Expandable>
</ParamField>

### Example

```typescript theme={null}
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

<ResponseField name="complete" type="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.
</ResponseField>

<ResponseField name="completed" type="Promise<void>">
  A promise that resolves when `complete` becomes `true`.

  Use this to wait for the image to be fully loaded before decoding:

  ```typescript theme={null}
  const decoder = new ImageDecoder({ data: stream, type: 'image/gif' });
  await decoder.completed;
  console.log('Image fully loaded, ready to decode');
  ```
</ResponseField>

<ResponseField name="type" type="string">
  The MIME type of the image being decoded.

  ```typescript theme={null}
  console.log(decoder.type); // 'image/jpeg'
  ```
</ResponseField>

## Static Methods

### isTypeSupported()

```typescript theme={null}
static isTypeSupported(type: string): Promise<boolean>
```

Check if a MIME type is supported for decoding.

<Accordion title="Example: Check format support">
  ```typescript theme={null}
  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
  ```
</Accordion>

**Parameters:**

* `type` (string): The MIME type to check

**Returns:** `Promise<boolean>` - Resolves to `true` if the format is supported

## Instance Methods

### decode()

```typescript theme={null}
decode(options?: ImageDecodeOptions): Promise<ImageDecodeResult>
```

Decode an image frame.

<Accordion title="Example: Basic decode">
  ```typescript theme={null}
  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();
  ```
</Accordion>

<Accordion title="Example: Animated GIF frame extraction">
  ```typescript theme={null}
  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');
  ```
</Accordion>

<Accordion title="Example: WebP animation processing">
  ```typescript theme={null}
  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));
  }
  ```
</Accordion>

**Parameters:**

<ParamField path="options" type="ImageDecodeOptions">
  Optional decode options.

  <Expandable title="ImageDecodeOptions properties">
    <ParamField path="frameIndex" type="number" default="0">
      The index of the frame to decode. For static images, this is always `0`. For animated images (GIF, WebP, AVIF), use this to decode specific frames.
    </ParamField>

    <ParamField path="completeFramesOnly" type="boolean" default="true">
      Whether to only return complete frames. When `true`, the promise will not resolve until the frame is fully decoded. When `false`, partial frames may be returned for progressive images.
    </ParamField>
  </Expandable>
</ParamField>

**Returns:** `Promise<ImageDecodeResult>` - Resolves to an object containing the decoded frame

### reset()

```typescript theme={null}
reset(): void
```

Reset the decoder state. Aborts any pending decode operations.

<Accordion title="Example: Reset and re-decode">
  ```typescript theme={null}
  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();
  ```
</Accordion>

**Returns:** `void`

### close()

```typescript theme={null}
close(): void
```

Close the decoder and release all resources. The decoder cannot be used after calling this method.

<Accordion title="Example: Proper cleanup">
  ```typescript theme={null}
  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();
  }
  ```
</Accordion>

**Returns:** `void`

## Interfaces

### ImageDecodeResult

The result of a decode operation.

<ResponseField name="image" type="VideoFrame" required>
  The decoded image as a `VideoFrame`. You must call `close()` on this frame when done to release memory.
</ResponseField>

<ResponseField name="complete" type="boolean" required>
  Whether the image is fully decoded. For animated images, this indicates whether this is the last frame.
</ResponseField>

### ImageDecodeOptions

Options for the `decode()` method.

<ResponseField name="frameIndex" type="number">
  The index of the frame to decode (0-based). Defaults to `0`.
</ResponseField>

<ResponseField name="completeFramesOnly" type="boolean">
  Whether to only return complete frames. Defaults to `true`.
</ResponseField>

### ImageDecoderInit

Configuration for creating an `ImageDecoder`.

<ResponseField name="data" type="BufferSource | ReadableStream<BufferSource>" required>
  The compressed image data.
</ResponseField>

<ResponseField name="type" type="string" required>
  The MIME type of the image.
</ResponseField>

<ResponseField name="colorSpaceConversion" type="'default' | 'none'">
  Color space conversion mode.
</ResponseField>

<ResponseField name="desiredWidth" type="number">
  Desired output width for scaling.
</ResponseField>

<ResponseField name="desiredHeight" type="number">
  Desired output height for scaling.
</ResponseField>

<ResponseField name="preferAnimation" type="boolean">
  Whether to prefer animated representation.
</ResponseField>

## See Also

<CardGroup cols={2}>
  <Card title="VideoFrame" icon="film" href="/api-reference/VideoFrame">
    Learn about the VideoFrame class returned by decode()
  </Card>

  <Card title="VideoEncoder" icon="clapperboard" href="/api-reference/VideoEncoder">
    Encode decoded images into video
  </Card>

  <Card title="Thumbnail Generation" icon="images" href="/cookbook/thumbnails">
    Generate thumbnails from images and videos
  </Card>

  <Card title="Video Transcoding" icon="shuffle" href="/cookbook/transcode">
    Complete transcoding workflow
  </Card>
</CardGroup>
