← All guides

CanvasTake · Canvas / Web Audio / GIF

Export canvas animations to GIF with CanvasTake

Author: Grundy · Checked: October 15, 2026

GIF export does not require real-time screen recording. CanvasTake 0.3.0 lets you render a timeline and submit frames to an exporter. Frame delays determine output duration, regardless of computer speed. GIF has no audio; choose video for narration.

1. Install a version with GIF support

Use @grundyjs/canvas-take 0.3.0 or later. The example imports /gif and /gif/gifenc separately. The latter supplies a ready-to-use adapter with a bundled codec; no separate gifenc installation is required. The ordinary video recorder import does not load the GIF codec.

npm install @grundyjs/canvas-take@^0.3.0

2. Prepare time-based rendering

Create a 2D canvas, for example 800 × 450. drawTimeline(time) is your function, not a CanvasTake export. It must redraw the entire canvas at the requested time in milliseconds. Clear the background each frame and keep canvas dimensions fixed throughout export. You can use an existing AlgiViz renderer.

3. Submit frames sequentially

A 50 ms interval gives 20 frames per second. The loop creates 120 frames for six seconds, followed by a final frame held for two seconds: 121 frames and eight seconds in total. Await each addFrame; concurrent operations are not supported. repeat: 0 loops forever.

import { createGifExporter } from '@grundyjs/canvas-take/gif';
import { createGifencEncoder } from '@grundyjs/canvas-take/gif/gifenc';

const controller = new AbortController();
const gif = createGifExporter({
    encoder: createGifencEncoder({ maxColors: 128, quality: 'high' }),
    repeat: 0,
    signal: controller.signal,
});
try {
    for (let time = 0; time < 6000; time += 50) {
        drawTimeline(time); // Your deterministic renderer draws into canvas.
        await gif.addFrame(canvas, 50);
    }
    drawTimeline(6000);
    await gif.addFrame(canvas, 2000); // Hold the final frame for two seconds.
    const blob = await gif.finish(); // image/gif; 8 seconds, 121 frames.
    // Download/preview blob; revoke any object URL when no longer needed.
} finally {
    gif.dispose();
}

4. Preview or download the Blob

finish() returns an image/gif Blob. Place the following code immediately after await gif.finish(), inside try where blob is in scope. Create an object URL for a download link and keep it alive while the link or preview is in use. Revoke it when that UI is removed. Call exporter.dispose() in finally; this does not invalidate an already returned Blob.

const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "animation.gif";
link.textContent = "Download GIF";
document.body.append(link);
// When removing the preview/download UI:
// link.remove();
// URL.revokeObjectURL(url);

5. Balance quality and output size

GIF supports palettes of up to 256 colors; 128 is a tradeoff rather than a universal setting. Durations round to 10 ms and some players clamp short delays. Transparency is composited onto a background, white by default. Bound resolution and frame count: encoded output and the Blob consume memory. The adapter runs on the calling thread without an automatic Worker. Call controller.abort() from a cancel button handler to cancel. Check file size, motion, colors and the final hold.