{"_id":"@cjser/sindresorhus__gifkit","_rev":"2-d9084bdbfe3b1c24d62d5c8e0bd9f6f8","name":"@cjser/sindresorhus__gifkit","dist-tags":{"latest":"0.1.1-cjser.2"},"versions":{"0.1.0-cjser.2":{"name":"@cjser/sindresorhus__gifkit","version":"0.1.0-cjser.2","keywords":["gif","graphics","image","encoder","decoder","render"],"author":{"url":"https://sindresorhus.com","name":"Sindre Sorhus","email":"sindresorhus@gmail.com"},"license":"MIT","_id":"@cjser/sindresorhus__gifkit@0.1.0-cjser.2","maintainers":[{"name":"nanahira","email":"nanahira@momobako.com"}],"xo":{"rules":{"max-lines":"off","no-bitwise":"off","unicorn/no-useless-spread":"off","unicorn/no-break-in-nested-loop":"off","node-test/no-conditional-assertion":"off"}},"dist":{"shasum":"c54d30becb0e476f2270a8b9feb66f273af7c80c","tarball":"https://registry.npmjs.org/@cjser/sindresorhus__gifkit/-/sindresorhus__gifkit-0.1.0-cjser.2.tgz","fileCount":15,"integrity":"sha512-OJJcLvpxPrVu9IWj/5BaCJxOrDXZQ200gsbAdpJMUu5ypetnqo9Qs550dWI7/IEI+gpXJ3hv8zHqPuz+T2Clqw==","signatures":[{"sig":"MEYCIQDVdXEhqPwIot4J6wr8U5Sq32n4Z1u09c5jeEC2U17u1AIhAPuZbOwTSoYY2eAj2Rfr6gHXXVoHL3HE5UBNFHvop6Je","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":212844},"main":"./dist-cjser/index.cjs","type":"module","cjser":{"original":{"name":"@sindresorhus/gifkit","files":["index.js","index.d.ts","source"],"exports":{"types":"./index.d.ts","default":"./index.js"},"scripts":{"test":"xo && node --test && tsd","benchmark":"node benchmark.js"},"version":"0.1.0","repository":"sindresorhus/gifkit"},"cjserVersion":2,"sourceVersion":"0.1.0"},"types":"./index.d.ts","engines":{"node":">=22"},"exports":{"types":"./index.d.ts","default":"./index.js","require":"./dist-cjser/index.cjs"},"funding":"https://github.com/sponsors/sindresorhus","gitHead":"a4bef54d7d22d3d9a6b84528457938d84cd65fe7","scripts":{"test":"xo && node --test && tsd","benchmark":"node benchmark.js"},"_npmUser":{"name":"nanahira","email":"nanahira@momobako.com"},"repository":{"url":"https://code.moenext.com/3rdeye/cjser.git","type":"git"},"_npmVersion":"11.9.0","description":"Encode, decode, and render GIF files","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","_hasShrinkwrap":false,"devDependencies":{"xo":"^4.0.0","tsd":"^0.33.0","jimp":"^1.6.1","tempy":"^3.2.0"},"_npmOperationalInternal":{"tmp":"tmp/sindresorhus__gifkit_0.1.0-cjser.2_1783615850344_0.8679359422545716","host":"s3://npm-registry-packages-npm-production"}},"0.1.1-cjser.2":{"name":"@cjser/sindresorhus__gifkit","version":"0.1.1-cjser.2","description":"Encode, decode, and render GIF files","license":"MIT","repository":{"type":"git","url":"https://code.moenext.com/3rdeye/cjser.git"},"funding":"https://github.com/sponsors/sindresorhus","author":{"name":"Sindre Sorhus","email":"sindresorhus@gmail.com","url":"https://sindresorhus.com"},"type":"module","exports":{"types":"./index.d.ts","require":"./dist-cjser/index.cjs","default":"./index.js"},"sideEffects":false,"engines":{"node":">=22"},"scripts":{"benchmark":"node benchmark.js","test":"xo && node --test && tsd"},"keywords":["gif","graphics","image","encoder","decoder","render"],"devDependencies":{"jimp":"^1.6.1","tempy":"^3.2.0","tsd":"^0.33.0","xo":"^4.0.0"},"xo":{"rules":{"no-bitwise":"off","max-lines":"off","unicorn/no-break-in-nested-loop":"off","unicorn/no-useless-spread":"off","node-test/no-conditional-assertion":"off"}},"types":"./index.d.ts","main":"./dist-cjser/index.cjs","cjser":{"sourceVersion":"0.1.1","cjserVersion":2,"original":{"name":"@sindresorhus/gifkit","version":"0.1.1","exports":{"types":"./index.d.ts","default":"./index.js"},"repository":"sindresorhus/gifkit","files":["index.js","index.d.ts","source"],"scripts":{"benchmark":"node benchmark.js","test":"xo && node --test && tsd"}}},"gitHead":"a4bef54d7d22d3d9a6b84528457938d84cd65fe7","_id":"@cjser/sindresorhus__gifkit@0.1.1-cjser.2","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-Xq4nZNGAffYXMhWlyDSJ4aShIw9twA7M6hG8cyNA3ce4qsh9tX+j86D4CztX8Vp8GQzioot4qOcmiPR7BWVFbQ==","shasum":"33c524df9dc13e6f6653d826367e47f199ce755c","tarball":"https://registry.npmjs.org/@cjser/sindresorhus__gifkit/-/sindresorhus__gifkit-0.1.1-cjser.2.tgz","fileCount":15,"unpackedSize":213155,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCxUg9fYmUyu7+ht1UHY4zXx1wHdjA2tFUmlPfGUmv9fAIhALow/SAd/9dCEqVJllVOaNy+AZ/tDWQyXfBlN9nppSpq"}]},"_npmUser":{"name":"nanahira","email":"nanahira@momobako.com"},"directories":{},"maintainers":[{"name":"nanahira","email":"nanahira@momobako.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sindresorhus__gifkit_0.1.1-cjser.2_1784135557001_0.8529363216007231"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-09T16:50:50.071Z","modified":"2026-07-15T17:12:37.230Z","0.1.0-cjser.2":"2026-07-09T16:50:50.483Z","0.1.1-cjser.2":"2026-07-15T17:12:37.123Z"},"author":{"name":"Sindre Sorhus","email":"sindresorhus@gmail.com","url":"https://sindresorhus.com"},"license":"MIT","keywords":["gif","graphics","image","encoder","decoder","render"],"repository":{"type":"git","url":"https://code.moenext.com/3rdeye/cjser.git"},"description":"Encode, decode, and render GIF files","maintainers":[{"name":"nanahira","email":"nanahira@momobako.com"}],"readme":"# gifkit\n\n> Encode, decode, and render GIF files\n\nThis package is a small JavaScript GIF toolkit. It can decode GIF87a/GIF89a files into structured blocks, encode structured GIF descriptions back to bytes, and render image blocks to RGBA pixels.\n\n## Install\n\n```sh\nnpm install @sindresorhus/gifkit\n```\n\n> [!NOTE]\n> This package works in both Node.js and the browser. It has no dependencies and uses only standard JavaScript and Web APIs.\n\n## Usage\n\n```js\nimport {decodeAnimatedGIF, encodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst bytes = encodeAnimatedGIF(frames, {\n\twidth: 640,\n\theight: 480,\n\tfps: 14,\n\tplayCount: 5,\n\tquality: 0.7,\n});\n\nconst animation = decodeAnimatedGIF(bytes);\n\nconsole.log(animation.frames[0].pixels);\nconsole.log(animation.frames[0].delay);\n```\n\n> [!NOTE]\n> If you accept untrusted input in a server context, it's up to you to enforce limits like timeouts and memory usage.\n\n## Common recipes\n\n### Encode RGBA frames to a GIF\n\n```js\nimport {encodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst bytes = encodeAnimatedGIF(frames, {\n\twidth: 640,\n\theight: 480,\n\tfps: 14,\n\tplayCount: 5,\n\tquality: 0.7,\n});\n```\n\nGIF stores frame timing in 0.01 second increments, so `fps` and `delay` values are rounded.\n\nFor photos and screenshots, keep the default `quality` or lower it. Use `quality: 1` only when every frame already has at most 256 exact colors.\n\n### Decode a GIF to RGBA frames\n\n```js\nimport {decodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst animation = decodeAnimatedGIF(bytes);\n\nconsole.log(animation.width);\nconsole.log(animation.height);\nconsole.log(animation.frames[0].pixels);\nconsole.log(animation.frames[0].delay);\n```\n\n### Decode and encode again\n\n```js\nimport {decodeAnimatedGIF, encodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst animation = decodeAnimatedGIF(bytes);\n\nconst options = {\n\twidth: animation.width,\n\theight: animation.height,\n};\n\nif (animation.playCount !== undefined) {\n\toptions.playCount = animation.playCount;\n}\n\nconst newBytes = encodeAnimatedGIF(animation.frames, options);\n```\n\n### Use per-frame delays\n\n```js\nimport {encodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst bytes = encodeAnimatedGIF([\n\t{pixels: frame1, delay: 0.1},\n\t{pixels: frame2, delay: 0.2},\n], {\n\twidth: 640,\n\theight: 480,\n\tplayCount: 5,\n\tquality: 0.7,\n});\n```\n\n## API\n\n### `decodeGIF(inputBytes, options?)`\n\nDecodes a `Uint8Array` containing a GIF file and returns a structured GIF object with logical-screen metadata, extension blocks, image blocks, color tables, and decoded indexed pixels.\n\nOptions:\n\n- `strict` - Default: `true`. Reject reserved bits, malformed extension sequencing, and trailing bytes. Use `false` for best-effort decoding.\n\nDecoding enforces internal pixel, block-count, data sub-block-count, and data payload byte limits to avoid resource exhaustion.\n\n### `decodeAnimatedGIF(inputBytes, options?)`\n\nDecodes an animated GIF to rendered full-frame RGBA frames. This is the easiest API when you want image buffers and frame timing instead of GIF internals.\n\n```js\nimport {decodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst animation = decodeAnimatedGIF(bytes);\n\nconsole.log(animation.width);\nconsole.log(animation.height);\nconsole.log(animation.playCount);\nconsole.log(animation.frames[0].pixels);\nconsole.log(animation.frames[0].delay);\n```\n\nReturns:\n\n- `width` / `height` - Logical screen size.\n- `playCount` - Total animation plays. `'forever'` means infinite playback. Omitted when no loop extension was present.\n- `frames` - Rendered full-frame RGBA frames.\n- `frames[].pixels` - `Uint8ClampedArray` of flat RGBA bytes: `[red, green, blue, alpha, ...]`.\n- `frames[].delay` - Frame delay in seconds. GIF stores delays in 0.01 second increments.\n\nOptions:\n\n- `background` - Default: `'transparent'`. Use `'gif'` to render the logical-screen background color.\n- `strict` - Default: `true`. Reject malformed decode data and render data like color indexes outside the active color table. Use `false` for best-effort decoding and rendering.\n\n### `encodeAnimatedGIF(frames, options)`\n\nEncodes RGBA frames as an animated GIF. This is the easiest API when you have a list of image buffers and want a normal animation.\n\n```js\nimport {encodeAnimatedGIF} from '@sindresorhus/gifkit';\n\nconst bytes = encodeAnimatedGIF(frames, {\n\twidth: 640,\n\theight: 480,\n\tfps: 14,\n\tplayCount: 5,\n\tquality: 0.7,\n});\n```\n\nOptions:\n\n- `width` / `height` - Required. Frame size.\n- `fps` - Frames per second for uniform timing. Cannot be combined with per-frame `delay`. GIF stores delays in 0.01 second increments, so timing is rounded to the nearest increment.\n- `playCount` - Total animation plays. Finite values must be integers from `1` to `65_536`. Use `'forever'` for infinite playback. Omit it to omit the loop extension. `playCount: 1` also omits the loop extension because that matches default GIF playback.\n- `quality` - Default: `0.8`. `0...1`, where lower values quantize each frame more aggressively to fit GIF’s 256-color palette. Quantization is per-frame and does not dither. For photos and screenshots, keep the default or lower it. Use `1` only when every frame already has at most 256 exact colors.\n\nFrames can be `Uint8Array` or `Uint8ClampedArray` RGBA pixels. Pixels are flat RGBA bytes: `[red, green, blue, alpha, ...]`.\n\nFor per-frame timing, use frame objects with `pixels` and `delay` in seconds. Delays are rounded to GIF’s 0.01 second increments:\n\n```js\nconst bytes = encodeAnimatedGIF([\n\t{pixels: frame1, delay: 0.1},\n\t{pixels: frame2, delay: 0.2},\n], {\n\twidth: 640,\n\theight: 480,\n\tplayCount: 5,\n\tquality: 0.7,\n});\n```\n\n### `encodeGIF(gif)`\n\nEncodes a structured GIF object to a `Uint8Array`. Use `image` blocks when you already have indexed pixels and a palette. Use `rgbaImage` blocks when you have flat RGBA bytes that fit GIF’s 256-color model.\n\nThe `gif` object has this shape:\n\n- `width` / `height` - Required. Logical screen size.\n- `globalColorTable` - Optional. Global palette as flat RGB bytes (`[red, green, blue, ...]`), a `Uint8Array`, or RGB triplets (`[[red, green, blue], ...]`).\n- `backgroundColorIndex` - Optional. Palette index used as the logical-screen background color. Requires `globalColorTable`.\n- `playCount` - Total animation plays. Finite values must be integers from `1` to `65_536`. Use `'forever'` for infinite playback. Omit it to omit the loop extension. `playCount: 1` also omits the loop extension because that matches default GIF playback.\n- `blocks` - Structured GIF blocks in file order.\n\nEncoded GIFs are always written as GIF89a.\n\nBlock types:\n\n- Indexed image block: `{type: 'image', width, height, pixels, colorTable?}`. `pixels` is one palette index per pixel and uses the image `colorTable` or GIF `globalColorTable`. `left` and `top` are optional image offsets and default to `0`. Advanced fields include `isInterlaced`.\n- RGBA image block: `{type: 'rgbaImage', width, height, pixels}`. `pixels` is flat RGBA bytes: `[red, green, blue, alpha, ...]`. gifkit builds a color table from the pixels, so this only works when the image has at most 256 colors and alpha values are fully transparent or fully opaque. `transparentColor` is the RGB palette value stored for transparent pixels.\n- Graphic control metadata for an image block: `graphicControlExtension: {disposalMethod?, delay?, transparentColorIndex?}`. `delay` is in seconds. `transparentColorIndex` is a palette index, or `undefined` for no transparent color. `disposalMethod` is exposed so structured GIF data can be preserved or re-encoded. It controls what happens to the current frame’s pixels before the next frame is drawn: `'unspecified'` leaves the choice to the decoder, `'keep'` keeps the pixels, `'restoreBackground'` clears them to the background, and `'restorePrevious'` restores the previous canvas. Rendered pixels already have this applied, so most users do not need to read or set it.\n- Comment extension: `{type: 'commentExtension', data}`. Strings must be ASCII.\n- Application extension: `{type: 'applicationExtension', identifier, authenticationCode, data?}`. `authenticationCode` accepts a 3-byte string or byte array. Decoded Netscape loop extensions also expose `isNetscapeLoopingExtension` and `playCount`. When encoding an explicit Netscape application extension block, finite `playCount` values must be from `2` to `65_536` because `1` is represented by omitting the loop extension.\n\nEncoding enforces internal pixel, block-count, encode work cost, data payload byte, and total encoded byte limits to avoid resource exhaustion.\n\nExtension payload strings must contain only ASCII characters. Use `Uint8Array` for binary extension data.\n\n### `renderGIFFrameSequence(gif, options?)`\n\nRenders image blocks as an iterable sequence of full logical-screen RGBA frames while applying transparency and disposal methods. Use this for playback or large GIFs where materializing every rendered frame would be wasteful.\n\n```js\nimport {decodeGIF, renderGIFFrameSequence} from '@sindresorhus/gifkit';\n\nconst gif = decodeGIF(bytes, {strict: false});\n\nfor (const frame of renderGIFFrameSequence(gif, {strict: false})) {\n\tconsole.log(frame.pixels);\n\tconsole.log(frame.delay);\n}\n```\n\nOptions:\n\n- `background` - Default: `'gif'`. Use `'transparent'` to render the logical-screen background as transparent.\n- `strict` - Default: `true`. Reject malformed render data like color indexes outside the active color table.\n- `repeat` - Default: `true`. Repeat according to GIF `playCount` metadata. Missing metadata means one pass, a finite number means that many total passes, and `'forever'` means infinite playback. Set to `false` to render exactly one pass.\n- `signal` - Optional abort signal. When aborted, iteration ends.\n\nYields:\n\n- `left` / `top` - Frame source offset in logical-screen pixels.\n- `width` / `height` - Frame source size.\n- `delay` - Frame delay in seconds.\n- `disposalMethod` - Advanced GIF compositing metadata kept so rendered frames can still be related back to the original GIF structure.\n- `pixels` - `Uint8ClampedArray` of rendered full logical-screen RGBA bytes.\n- `index` - Zero-based image-frame index within the current pass.\n- `loopIndex` - Zero-based repetition index for this frame.\n\n### `renderGIFFrames(gif, options?)`\n\nRenders image blocks to full logical-screen RGBA frames while applying transparency and disposal methods. Most users should use `decodeAnimatedGIF()` instead; use this when you need decoded GIF structure and rendered pixels.\n\nOptions:\n\n- `background` - Default: `'gif'`. Use `'transparent'` to render the logical-screen background as transparent.\n- `strict` - Default: `true`. Reject malformed render data like color indexes outside the active color table.\n\nReturns:\n\n- `width` / `height` - Logical screen size.\n- `playCount` - Total animation plays. `'forever'` means infinite playback. Omitted when no loop extension was present.\n- `frames` - Rendered full logical-screen RGBA frames.\n- `frames[].left` / `frames[].top` - Frame source offset in logical-screen pixels.\n- `frames[].width` / `frames[].height` - Frame source size.\n- `frames[].delay` - Frame delay in seconds.\n- `frames[].disposalMethod` - Advanced GIF compositing metadata kept so rendered frames can still be related back to the original GIF structure. `'unspecified'` leaves the choice to the decoder, `'keep'` keeps the pixels, `'restoreBackground'` clears them to the background, and `'restorePrevious'` restores the previous canvas before the next frame is drawn. gifkit already applies this when producing `frames[].pixels`, so use it only if you need to inspect or preserve the original animation metadata.\n- `frames[].pixels` - `Uint8ClampedArray` of rendered full logical-screen RGBA bytes.\n\nRendering enforces internal logical-screen, rendered-output, render block-count, and image-block pixel limits to avoid huge allocations.\n\n### `indexedImage(pixels, options?)`\n\nUse this when you have raw RGBA pixels, for example from a canvas or image decoder, and want the `pixels` and `colorTable` needed for an image block. It only works when the pixels already fit GIF’s palette model.\n\nConverts RGBA pixels to GIF indexed pixels and a power-of-two color table without quantizing or dithering. Each unique opaque RGB color becomes a palette entry. All fully transparent pixels share one palette entry and set `transparentColorIndex`.\n\nThrows if the image exceeds the internal pixel limit, uses more than 256 palette entries, or uses partial alpha. GIF only supports fully transparent or fully opaque pixels.\n\nOptions:\n\n- `transparentColor` - Default: `[0, 0, 0]`. RGB value stored in the palette entry used for transparent pixels.\n\n```js\nimport {indexedImage} from '@sindresorhus/gifkit';\n\nconst image = indexedImage(new Uint8ClampedArray([\n\t255, 0, 0, 255,\n\t0, 0, 0, 0,\n]));\n\nconsole.log(image.pixels);\nconsole.log(image.colorTable);\n```\n\n## Intentionally unsupported GIF spec features\n\ngifkit focuses on GIF features that are useful in modern JavaScript workflows. It intentionally does not expose the GIF user-input flag, pixel aspect ratio byte, color-resolution metadata, color-table sort flags, or Plain Text Extension rendering/encoding. These are legacy display-era features, are rarely present in real GIFs, and are easy to misunderstand. Unknown extensions, including Plain Text Extensions, are preserved as `unknownExtension` blocks.\n\n## cjser\n\nThis package is a CommonJS-compatible build generated by cjser for projects that still need `require()` support. The source version matches the original npm package version, with a cjser prerelease suffix for this generated build.\nOriginal repository: https://github.com/sindresorhus/gifkit\n","readmeFilename":"readme.md"}