{"id":18486936,"url":"https://github.com/benjaminadk/gif-encoder-2","last_synced_at":"2025-04-14T08:15:06.413Z","repository":{"id":47417425,"uuid":"190976124","full_name":"benjaminadk/gif-encoder-2","owner":"benjaminadk","description":"Encode GIFs with Node","archived":false,"fork":false,"pushed_at":"2023-12-08T14:24:19.000Z","size":11105,"stargazers_count":62,"open_issues_count":18,"forks_count":18,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-27T21:52:01.059Z","etag":null,"topics":["electron","gif","neuquant-algorithm","nodejs","octree-algorithms"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benjaminadk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-09T07:18:56.000Z","updated_at":"2025-03-21T07:56:16.000Z","dependencies_parsed_at":"2024-06-18T15:22:10.288Z","dependency_job_id":"8a07e3f8-5e23-45d4-88c1-7d744881590c","html_url":"https://github.com/benjaminadk/gif-encoder-2","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benjaminadk%2Fgif-encoder-2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benjaminadk%2Fgif-encoder-2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benjaminadk%2Fgif-encoder-2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benjaminadk%2Fgif-encoder-2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benjaminadk","download_url":"https://codeload.github.com/benjaminadk/gif-encoder-2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248843951,"owners_count":21170495,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electron","gif","neuquant-algorithm","nodejs","octree-algorithms"],"created_at":"2024-11-06T12:49:58.384Z","updated_at":"2025-04-14T08:15:06.378Z","avatar_url":"https://github.com/benjaminadk.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# gif-encoder-2\n\nEncode GIFs with Node.js\n\n## Contents\n\n- [Installation](#installation)\n- [Overview](#overview)\n- [Usage](#usage)\n  - [Constructor](#constructor)\n  - [Methods](#methods)\n- [Examples](#examples)\n  - [Canvas Animation](#canvas-animation)\n  - [Sequencial Images](#sequencial-images)\n- [Algorithms](#algorithms)\n- [Optimizer](#optimizer)\n- [Progess Event](#progress-event)\n\n## Installation\n\n```\nnpm install gif-encoder-2\n```\n\n## Overview\n\nThis library builds on top of previous _JavaScript_ _GIF_ encoders including [jsgif](https://github.com/antimatter15/jsgif) and [gifencoder](https://github.com/eugeneware/gifencoder).\n\nThis library adds the [Octree](https://en.wikipedia.org/wiki/Octree) quantization algorithm as an alternative to the original _NeuQuant_ algorithm.\n\nThis library adds a simple optimizer to speed up overall processing time of both algorithms.\n\nThis library adds a progress event.\n\nThis library is designed to be used in a _Node_ environment, including the [Electron](https://electronjs.org/) renderer process. [Node Canvas](https://github.com/Automattic/node-canvas) can be a useful peer library but isn't required. The [HTML Canvas API] can be used in _Electron_.\n\n## Usage\n\n### Constructor\n\n`GIFEncoder(width, height, algorithm, useOptimizer, totalFrames)`\n\n|   Parameter    |  Type   |          Description           | Required |  Default   |\n| :------------: | :-----: | :----------------------------: | :------: | :--------: |\n|    `width`     | number  | the width of images in pixels  |   yes    |    n/a     |\n|    `height`    | number  | the height of images in pixels |   yes    |    n/a     |\n|  `algorithm`   | string  |     `neuquant` or `octree`     |    no    | `neuquant` |\n| `useOptimizer` | boolean |   enables/disables optimizer   |    no    |   false    |\n| `totalFrames`  | number  |     total number of images     |    no    |     0      |\n\n```javascript\nconst encoder = new GIFEncoder(500, 500)\nconst encoder = new GIFEncoder(1200, 800, 'octree', false)\nconst encoder = new GIFEncoder(720, 480, 'neuquant', true, 20)\n```\n\n### Methods\n\n|        Method        |    Parameter     |               Description               |                           Notes                            |\n| :------------------: | :--------------: | :-------------------------------------: | :--------------------------------------------------------: |\n|       `start`        |       n/a        |           Starts the encoder            |                            n/a                             |\n|      `addFrame`      | `Canvas Context` |         Adds a frame to the GIF         |                            n/a                             |\n|      `setDelay`      |      number      | Number of milliseconds to display frame |                Can be set once or per frame                |\n| `setFramesPerSecond` |      number      | Number of frames per second to display  |                  Another way to set delay                  |\n|     `setQuality`     |   number 1-30    |            Neuquant quality             |                     1 is best/slowest                      |\n|    `setThreshold`    |   number 0-100   |     Optimizer threshold percentage      | Color table reused if current frame matches previous frame |\n|     `setRepeat`      |   number \u003e= 0    |        Number of loops GIF does         |   0 is forever, anything else if literal number of loops   |\n|       `finish`       |       n/a        |            Stops the encoder            |              Call after all frames are added               |\n\n## Examples\n\n### Canvas Animation\n\nDraw a square that changes color as it moves.\n\n```javascript\nconst GIFEncoder = require('gif-encoder-2')\nconst { createCanvas } = require('canvas')\nconst { writeFile } = require('fs')\nconst path = require('path')\n\nconst size = 200\nconst half = size / 2\n\nconst canvas = createCanvas(size, size)\nconst ctx = canvas.getContext('2d')\n\nfunction drawBackground() {\n  ctx.fillStyle = '#ffffff'\n  ctx.fillRect(0, 0, size, size)\n}\n\nconst encoder = new GIFEncoder(size, size)\nencoder.setDelay(500)\nencoder.start()\n\ndrawBackground()\nctx.fillStyle = '#ff0000'\nctx.fillRect(0, 0, half, half)\nencoder.addFrame(ctx)\n\ndrawBackground()\nctx.fillStyle = '#00ff00'\nctx.fillRect(half, 0, half, half)\nencoder.addFrame(ctx)\n\ndrawBackground()\nctx.fillStyle = '#0000ff'\nctx.fillRect(half, half, half, half)\nencoder.addFrame(ctx)\n\ndrawBackground()\nctx.fillStyle = '#ffff00'\nctx.fillRect(0, half, half, half)\nencoder.addFrame(ctx)\n\nencoder.finish()\n\nconst buffer = encoder.out.getData()\n\nwriteFile(path.join(__dirname, 'output', 'beginner.gif'), buffer, error =\u003e {\n  // gif drawn or error\n})\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/benjaminadk/gif-encoder-2/master/examples/output/beginner.gif\" /\u003e\n\u003c/p\u003e\n\n### Sequencial Images\n\nCreate a function that reads a directory of images and turns them into a _GIF_.\n\n```javascript\nconst GIFEncoder = require('gif-encoder-2')\nconst { createCanvas, Image } = require('canvas')\nconst { createWriteStream, readdir } = require('fs')\nconst { promisify } = require('util')\nconst path = require('path')\n\nconst readdirAsync = promisify(readdir)\nconst imagesFolder = path.join(__dirname, 'input')\n\nasync function createGif(algorithm) {\n  return new Promise(async resolve1 =\u003e {\n    // read image directory\n    const files = await readdirAsync(imagesFolder)\n\n    // find the width and height of the image\n    const [width, height] = await new Promise(resolve2 =\u003e {\n      const image = new Image()\n      image.onload = () =\u003e resolve2([image.width, image.height])\n      image.src = path.join(imagesFolder, files[0])\n    })\n\n    // base GIF filepath on which algorithm is being used\n    const dstPath = path.join(__dirname, 'output', `intermediate-${algorithm}.gif`)\n    // create a write stream for GIF data\n    const writeStream = createWriteStream(dstPath)\n    // when stream closes GIF is created so resolve promise\n    writeStream.on('close', () =\u003e {\n      resolve1()\n    })\n\n    const encoder = new GIFEncoder(width, height, algorithm)\n    // pipe encoder's read stream to our write stream\n    encoder.createReadStream().pipe(writeStream)\n    encoder.start()\n    encoder.setDelay(200)\n\n    const canvas = createCanvas(width, height)\n    const ctx = canvas.getContext('2d')\n\n    // draw an image for each file and add frame to encoder\n    for (const file of files) {\n      await new Promise(resolve3 =\u003e {\n        const image = new Image()\n        image.onload = () =\u003e {\n          ctx.drawImage(image, 0, 0)\n          encoder.addFrame(ctx)\n          resolve3()\n        }\n        image.src = path.join(imagesFolder, file)\n      })\n    }\n  })\n}\n\ncreateGif('neuquant')\ncreateGif('octree')\n```\n\n**NeuQuant Algorithm**\n\n\u003cimg src=\"https://raw.githubusercontent.com/benjaminadk/gif-encoder-2/master/examples/output/intermediate-neuquant.gif\" /\u003e\n\n**Octree Algorithm**\n\n\u003cimg src=\"https://raw.githubusercontent.com/benjaminadk/gif-encoder-2/master/examples/output/intermediate-octree.gif\" /\u003e\n\n## Algorithms\n\n- _NeuQuant_ tends to perform faster than _Octree_\n- _Octree_ tends to output a smaller file than _NeuQuant_\n- _Octree_ produces a slight banding effect\n\nThe example above encodes 20 images measuring 300px x 240px. The output file from _NeuQuant_ is 1172KB and the _Octree_ is less than half of that at 515KB.\n\n## Optimizer\n\nThe optimizer works by reusing the color palette from the previous image on the current image. This can reduce the overall processing time signifigantly but its best suited for a sequence of similarly colored images. Use the `setThreshold` method to set a percentage determining how similar the two images must be to trigger the optimizer. The default is `90%`. The optimizer is only used if `true` is passed as the 4th argument to the constructor.\n\n## Progress Event\n\nWorks if `totalFrames` is expressed in constructor, otherwise this value will be 0.\n\n```javascript\nencoder.on('progress', percent =\u003e {\n  // do something with percent value\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenjaminadk%2Fgif-encoder-2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenjaminadk%2Fgif-encoder-2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenjaminadk%2Fgif-encoder-2/lists"}