Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

FFmpeg can add a caption to every frame of an animated GIF with its drawtext filter. For a reliable GIF maker, render the text first, generate a palette from that rendered video, then apply the palette while writing the GIF. This order keeps caption colors in the palette and avoids much of the blur and color damage caused by direct GIF encoding.

What you need

  • A current FFmpeg build with the drawtext filter and --enable-libfreetype. Fontconfig, FriBidi and HarfBuzz add font fallback and complex-text shaping when available.
  • An animated GIF or another video input.
  • A font file you are licensed to distribute or embed. Supplying an explicit file with fontfile= makes rendering consistent between machines.
  • A shell that can read the commands below and write the output directory.

Check your build with ffmpeg -filters | grep drawtext. If no filter is listed, install an FFmpeg package built with FreeType support rather than trying to work around the missing filter.

The basic caption command

This command centers white text near the bottom of every frame and uses a two-pass palette workflow:

ffmpeg -i input.gif -filter_complex "[0:v]drawtext=fontfile=/path/font.ttf:text='Hello':fontcolor=white:fontsize=36:x=(w-text_w)/2:y=h-text_h-20,split[a][b];[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse" -loop 0 output.gif

Replace /path/font.ttf, the input name and the caption. The expression x=(w-text_w)/2 centers the text horizontally. y=h-text_h-20 places its bottom edge 20 pixels above the lower edge of the frame. The split sends the text-rendered stream to two branches: palettegen creates one palette for the complete animation and paletteuse uses it to encode the GIF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Gif Maker - Mp4 to gif - Gif Editor
  • Convert videos to high-quality GIFs with ease.
  • Customize GIF settings including frame rate, resolution, and looping.
  • Intuitive and user-friendly interface for effortless GIF creation.
  • Trim videos to the perfect length for your GIFs.
  • Save and share GIFs directly from the app.

-loop 0 writes an infinitely looping GIF. Omit or change the value when your target application needs different loop behavior.

Build a caption pipeline step by step

1. Inspect the source

Before rendering, identify the source dimensions, frame rate and duration:

ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,duration -of default=noprint_wrappers=1 input.gif

Decide whether the maker should preserve those values or deliberately resize, trim or change playback speed. GIFs use a limited palette, so the final display size is also the size at which you should judge readability.

2. Choose placement and styling

Useful drawtext expressions include:

  • Top center: x=(w-text_w)/2:y=20
  • Top-left safe area: x=20:y=20
  • Bottom-right safe area: x=w-text_w-20:y=h-text_h-20
  • Vertical center: x=(w-text_w)/2:y=(h-text_h)/2

Add contrast when the background changes:

drawtext=fontfile=/path/font.ttf:text='Hello':fontcolor=white:fontsize=36:borderw=3:bordercolor=black:x=(w-text_w)/2:y=h-text_h-20

A border is often more robust than a translucent box, but a box can be added with box=1:[email protected]:boxborderw=12. Keep the caption inside a safe margin so platform cropping does not cut it off.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

3. Handle real text safely

Inline text= becomes fragile when a caption contains colons, backslashes, apostrophes, commas or shell metacharacters. Put UTF-8 text in a file instead:

printf '%s' 'Status: ready — 100%' > caption.txt
ffmpeg -i input.gif -filter_complex "[0:v]drawtext=fontfile=/path/font.ttf:textfile=caption.txt:fontcolor=white:fontsize=36:x=(w-text_w)/2:y=h-text_h-20,split[a][b];[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse" -loop 0 output.gif

textfile avoids most shell quoting problems. Ensure the file is UTF-8 and escape characters required by FFmpeg filter syntax. If a non-Latin script renders as boxes or disconnected glyphs, use a font containing those glyphs and a build with the shaping libraries mentioned above.

Palette generation, dithering and transparency

A GIF cannot retain the unrestricted color range of the source video. palettegen analyzes the rendered stream and paletteuse maps each frame to that palette. Generating the palette after drawtext ensures the caption’s colors are considered.

ffmpeg -i input.mp4 -filter_complex "[0:v]drawtext=fontfile=/path/font.ttf:textfile=caption.txt:fontcolor=white:fontsize=36:x=20:y=20,split[a][b];[a]palettegen=max_colors=256:reserve_transparent=1[p];[b][p]paletteuse=dither=sierra2_4a" -loop 0 output.gif
  • max_colors controls palette size. Lower values can reduce color detail.
  • reserve_transparent=1 preserves a transparent palette entry when transparency matters.
  • transparency_color controls the color used for transparent areas during palette generation.
  • stats_mode changes whether palette statistics favor the whole stream, each frame or changing regions.
  • dither selects the pattern used when source colors fall between palette entries. Test alternatives on gradients and moving backgrounds; dithering can trade banding for visible grain.

For a photographic or highly animated source, inspect several frames rather than judging only the first one. A palette optimized for one scene may make another scene or a bright caption look poor.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

GIF input, delays and looping

FFmpeg’s animated GIF demuxer exposes delay controls in hundredths of a second. -ignore_loop 0 tells FFmpeg to honor the input GIF’s loop behavior while reading it:

ffmpeg -ignore_loop 0 -i input.gif -filter_complex "[0:v]drawtext=fontfile=/path/font.ttf:text='Loop aware':fontcolor=white:fontsize=32:x=20:y=20,split[a][b];[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse" -loop 0 output.gif

Use -min_delay and -max_gif_delay when you must constrain unusually short or long frame delays. Validate the resulting duration in the player that matters to your users; different platforms may handle GIF loops and timing differently.

Rank #2
Sale
Viondra Rechargeable Crab Bubble Machine Toy, Automatic Obstacle Avoidance
  • MOVING CRAB BUBBLE FUN: This crab bubble machine moves around as it blows bubbles, giving kids a playful toy for birthdays, backyard games, family gatherings, and outdoor parties
  • TURNS AWAY FROM OBSTACLES: Built-in moving wheels help the crawling crab bubble maker change direction when it meets an obstacle, keeping the bubble fun active on smooth, flat surfaces
  • LIGHTS, MUSIC AND BUBBLES: Colorful lights and cheerful music create an engaging bubble show for toddlers and kids during supervised indoor or outdoor play
  • RECHARGEABLE USB-C DESIGN: Built-in rechargeable battery and included USB-C cable make this bubble blower easy to power up before playtime; best used on flat floors, patios, or short grass
  • GIFT-READY SET FOR AGES 3+: Includes crab bubble machine, USB-C charging cable, 2 bubble solutions, and colorful packaging for birthdays, holidays, Easter baskets, and summer outdoor toys

Turning the command into a GIF maker

A web or desktop maker can expose a small set of controls while keeping the FFmpeg graph predictable:

  1. Accept an upload or source URL and normalize it to a temporary local file.
  2. Validate dimensions, duration and frame count before processing; reject unexpectedly large inputs to protect memory and disk.
  3. Store caption text as a UTF-8 file, not an interpolated shell string.
  4. Validate font paths against an allowlist. Do not pass arbitrary user input directly to a shell command.
  5. Build drawtext arguments from typed values for position, size, color, border and opacity.
  6. Run FFmpeg with an argument array or safely escaped process API, capture stderr, and impose a timeout.
  7. Write to a temporary output, verify it can be decoded, then atomically move it to its final name.
  8. Delete temporary uploads, caption files and intermediate images after success or failure.

For concurrent jobs, cap workers according to CPU and memory, and give each job an isolated temporary directory. Full-page processing of large GIFs can be expensive because the palette pass reads the stream and the palette-use pass encodes it again.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quality and performance decisions

Dimensions and frame rate

Preserving source dimensions and frame rate gives the most faithful motion but increases file size and processing time. Resizing before palette generation reduces both. If you resize, do it before drawtext so placement expressions use the final dimensions:

ffmpeg -i input.gif -filter_complex "[0:v]scale=640:-1:flags=lanczos,drawtext=fontfile=/path/font.ttf:text='Caption':fontcolor=white:fontsize=32:x=(w-text_w)/2:y=h-text_h-16,split[a][b];[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse" -loop 0 output.gif

Font consistency

System font lookup can differ across containers and operating systems. Bundle a licensed font and reference it with an absolute path. Test punctuation, accented characters and the scripts your users need.

File size

Reducing dimensions, frame rate, duration or palette colors generally lowers output size. Do not reduce palette colors blindly: a smaller file with banding or unreadable text is usually a worse result. Measure representative inputs and set limits appropriate to your deployment.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

“No such filter: drawtext”

Your FFmpeg build lacks the required filter or FreeType support. Install a build compiled with --enable-libfreetype, then confirm with ffmpeg -filters.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Text is missing or appears as squares

Check the font path, file permissions and glyph coverage. Use an explicit licensed font with fontfile=. For complex scripts, install a build with fontconfig, FriBidi and HarfBuzz support.

The command fails on punctuation

Move the caption to a UTF-8 textfile. Shell quoting and FFmpeg filter escaping are separate layers; an argument that is valid in one shell may fail in another.

Colors look wrong or gradients band

Ensure palettegen runs after drawtext and that its output feeds paletteuse. Try a different dither, adjust stats_mode, or retain more colors with max_colors.

Transparency has a solid background

Use reserve_transparent=1 and inspect whether earlier filters removed the alpha channel. Confirm that the destination player supports transparent GIF pixels.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Lifeprint Wifi and Bluetooth 3'X4.5 Instant photo Printer (LP002-3WHT) White - Renewed
  • SUPER SIMPLE TO USE: Easily print real photos from your phone with just one click.
  • AUGMENTED REALITY: Lifeprint’s Augmented Reality Hyperphotos can embed a video inside your photos so they come to life like magic in your hands. Just think magical Harry Potter photos.
  • WORLD’S ONLY SHAREABLE PHOTO PRINTER: Connect with friends and family within the Lifeprint App and instantly share real photos to them from anywhere in the world. No other photo printer in the world can do this.
  • PRINT FROM SOCIAL MEDIA: Print saved Snaps, Facebook, Instagram, Apple Live Photos, moving GIFs, and more. Just imagine the cat video possibilities…
  • LARGE FORMAT PRINTS: The larger 3x4.5 photo print is over twice the size of other 2x3 inch photo printers. The larger size really makes a difference.

Playback loops or ends unexpectedly

Check -ignore_loop when reading the source and -loop when writing. Also inspect frame delays with ffprobe; very short delays may be clamped by players.

Processing is too slow or output is too large

Resize, trim or lower the frame rate before palette generation. Apply job timeouts and concurrency limits, and keep temporary files on fast local storage.

Or skip the browser setup

If your goal is to capture a web page before adding captions, ScreenshotNeo returns a PNG, JPEG, WebP or PDF from one request. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers report the page verdict and billing result.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for options such as full-page capture, CSS selectors, custom CSS and JavaScript, device presets, dark mode, PDF output, waiting rules, request blocking, cookies, headers, geolocation, caching, signed links, asynchronous webhooks and bulk capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

It also provides an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free for ScreenshotNeo.

FAQ

Can FFmpeg add different text at different times?

Yes. Use time expressions and conditional logic in drawtext, or chain multiple drawtext filters with separate enable expressions.

Can I output a video instead of a GIF?

Yes. Keep the drawtext filter and remove the GIF-specific palette pipeline, then select a video encoder and container appropriate to your target.

Why does a GIF maker need two palette filters?

palettegen creates the color table and paletteuse applies it. The split graph lets both filters operate on the same text-rendered stream.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can FFmpeg add different text at different times?

Yes. Use time expressions and conditional logic in drawtext, or chain multiple drawtext filters with separate enable expressions.

Can I output a video instead of a GIF?

Yes. Keep the drawtext filter and remove the GIF-specific palette pipeline, then select a video encoder and container appropriate to your target.

Quick Recap

Bestseller No. 1
Gif Maker - Mp4 to gif - Gif Editor
Gif Maker - Mp4 to gif - Gif Editor
Convert videos to high-quality GIFs with ease.; Customize GIF settings including frame rate, resolution, and looping.
$3.40
SaleBestseller No. 3
Lifeprint Wifi and Bluetooth 3'X4.5 Instant photo Printer (LP002-3WHT) White - Renewed
Lifeprint Wifi and Bluetooth 3'X4.5 Instant photo Printer (LP002-3WHT) White - Renewed
SUPER SIMPLE TO USE: Easily print real photos from your phone with just one click.
$49.99

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.