npm package: @la-moore/gif
Description: TypeScript library for working with GIFs in the browser.
Author: moorlex
Latest version: 1.0.0

# @la-moore/gif

TypeScript library for working with GIFs in the browser:

- **Encoding (creating) animations** with `GifWriter`.
- **Decoding (reading) GIFs** with `GifReader`.

The encoder is optimized for browser usage: the heavy work (quantization/palette) is performed in a **Web Worker** so it doesn’t block the UI.

## Features

- **Create GIFs from `HTMLCanvasElement`/`HTMLImageElement`.**
- **Create GIFs from `ImageData`** (when frames are already prepared).
- **Render progress** (`onRenderProgress`).
- **Output as `Blob`** (`getBlobGIF`) or **Data URL (base64)** (`getBase64GIF`).
- **Frame delay control** (milliseconds) and `repeat` (loop count).
- **Dithering / palette / transparency / disposal** (globally and per-frame).
- **Playback modes**:
  - `loop` (default)
  - `reverse`
  - `bounce`
- **Read GIFs**: load from `Uint8Array`/`ArrayBuffer`/`Blob`, iterate frames with RGBA decode.

## Benefits

- **Doesn’t block the main thread** while preparing frames (worker pool).
- **Simple API**: add frames -> get a `Blob`/base64.
- **TypeScript types** included.
- **No DOM framework dependency** (React/Vue not required).

## Installation

```sh
npm i @la-moore/gif
```

or

```sh
pnpm add @la-moore/gif
```

or

```sh
yarn add @la-moore/gif
```

## Quick start (create a GIF)

```ts
import { GifWriter } from '@la-moore/gif'

const gif = new GifWriter({
  width: 320,
  height: 240,
  numWorkers: 2,
})

gif.setDelay(250) // milliseconds
gif.setRepeat(0)  // 0 = loop forever

gif.addFrame(canvas) // HTMLCanvasElement or HTMLImageElement

gif.getBlobGIF((blob) => {
  const url = URL.createObjectURL(blob)
  // e.g. img.src = url
})
```

## Render progress

```ts
const gif = new GifWriter({ width: 320, height: 240 })

gif.onRenderProgress((p) => {
  // p: 0..1
  console.log('progress', Math.round(p * 100) + '%')
})

gif.addFrame(canvas)

gif.getBlobGIF((blob) => {
  console.log('done', blob.size)
})
```

## Playback modes (loop / reverse / bounce / once)

```ts
const gif = new GifWriter({ width: 320, height: 240, playbackMode: 'bounce' })

// you can change it later
gif.setPlaybackMode('reverse')
```

Note: the mode affects the frame order/generation before rendering (e.g. `bounce` adds a reverse pass without duplicating the first/last frame).

## Add a frame with per-frame options

```ts
gif.addFrame(canvas, {
  delay: 120,
  disposal: 2,
  transparencyCutOff: 0.7,
  // palette / dithering can also be set here
})
```

## Add a frame as ImageData

If you generate frames yourself (e.g. from WebGL/OffscreenCanvas) and already have `ImageData`:

```ts
gif.addFrameImageData(imageData, { delay: 80 })
```

## Get base64 (Data URL)

```ts
gif.getBase64GIF((dataUrl) => {
  // data:image/gif;base64,...
  img.src = dataUrl
})
```

## Cleanup and resource management

- `reset()` clears frames and resets render state (the instance remains usable).
- `destroy()` terminates workers. After calling it, you should not keep using the instance.

```ts
gif.reset()
gif.destroy()
```

## Read a GIF (GifReader)

```ts
import { GifReader } from '@la-moore/gif'

const res = await fetch('/example.gif')
const buffer = await res.arrayBuffer()

const reader = new GifReader()
reader.loadArrayBuffer(buffer)

console.log(reader.width, reader.height)
console.log('frames', reader.framesCount)
console.log('duration (centiseconds)', reader.duration)

reader.forEach((frame, i, rgba) => {
  // frame: frame metadata (omggif)
  // rgba: Uint8ClampedArray, size width*height*4
  console.log('frame', i, frame)
})
```

You can also load from a `Blob`:

```ts
const reader = new GifReader()
await reader.loadBlob(fileBlob)
```

## Notes on Web Worker and bundlers

Inside the library, the worker is created like this:

```ts
new Worker(new URL('./gif.worker.js', import.meta.url), { type: 'module' })
```

That means:

- **Vite** automatically detects the worker and bundles it as a separate chunk.
- For **Webpack/Next.js**, make sure your build supports `new URL(..., import.meta.url)` for workers (in modern versions this is usually fine). If you use SSR, import and create `GifWriter` only on the client because it relies on `document`, `canvas`, and `Worker`.

## Build (for maintainers)

```sh
npm run build
```