akhi07rx

4 min read

Built a Tool to Turn Webpages Into a GIF


Frames Extract

Sometimes the best projects start with a weird question. Mine was: can I just… screenshot a webpage over and over, really fast, and stitch it into a GIF? 👀

Turns out, yes. And it works surprisingly well.

HTML2GIF is a tiny Node.js script. Give it a URL, get a GIF. Headless Chrome does the screenshotting, ffmpeg does the stitching.

§How It Actually Works

The core of it is Puppeteer, which lets you control a real Chrome instance from Node. The script navigates to the page, resizes the viewport to 4K 11(because why not) :) , aand then takes a screenshot API every 100ms for 2 seconds. That gives you 20 frames to work with.

const interval = 100;
const duration = 2000;
const frameCount = duration / interval;

for (let i = 0; i < frameCount; i++) {
  await page.screenshot({ path: `${tempFilePath}-${i}.png`, type: "png" });
  await new Promise((resolve) => setTimeout(resolve, interval));
  bar.tick();
}

Once the frames are captured, ffmpeg takes over. It runs in two passes: first it generates an optimized color palette from the frames, then it uses that palette to encode the final GIF. This two-pass approach is what keeps the colors from looking washed out, which is a common problem when naively converting video to GIF.

ffmpeg()
  .input(`${tempFilePath}-%d.png`)
  .complexFilter("fps=30,palettegen")
  .output(`${tempFilePath}-palette.png`)

The temp PNGs get cleaned up afterward. You’re left with a single .gif file inside an Output/ folder named after the page title.

§Setting It Up

You’ll need Node.js and npm. After that it’s just one install command:

npm install puppeteer @ffmpeg-installer/ffmpeg progress

The @ffmpeg-installer/ffmpeg package handles the ffmpeg binary automatically, so you don’t have to mess with system installs. Run the script, paste in a URL when prompted, and watch the progress bar fill up.

node convert.js

One thing to note: the script is hardcoded to use Chrome from the default Windows installation path. If you’re on a different OS or have Chrome installed somewhere else, you’ll need to update this line:

const CHROME_PATH =
  "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";

§What It’s Good For

Honestly this thing is great for capturing live demos, animated UI components, or anything with a loading sequence you want to show off without recording your whole screen. It’s also just a fun party trick.

It’s a basic implementation and won’t handle every edge case. Pages that rely heavily on AJAX or need time to settle after load might need some adjustments to the timing. And generating GIFs from 4K screenshots does chew through memory, so keep that in mind if you’re running it on something modest.

But for a weekend project built out of curiosity? Pretty happy with how it turned out.

The full code is on GitHub. MIT licensed, so do whatever you want with it.

P.S. This is very much a “it worked on my machine” kind of project. The timing is hardcoded, the Chrome path is hardcoded, and I’m sure there are edge cases I haven’t thought of. If you’re a puppeteer or ffmpeg person and something here made you wince, I’d genuinely love to know a better way to do it. Still learning. :)