DEV Community

Javid Jamae
Javid Jamae

Posted on • Originally published at ffmpeg-micro.com

FFmpeg drawtext Filter: Complete Guide to Text Overlays

Originally published at ffmpeg-micro.com

The drawtext filter is the most parameter-heavy filter in FFmpeg. The official documentation reads like a specification sheet, not a guide. I've spent enough time decoding it that I figured I'd write the reference I wish existed.

If you just need to add text quickly, see our step-by-step guide. This post is the full parameter breakdown for when you need precise control.

Basic drawtext Syntax

The minimum viable drawtext command needs just a text string:

ffmpeg -i input.mp4 -vf "drawtext=text='Hello World'" output.mp4
Enter fullscreen mode Exit fullscreen mode

That renders white text in the top-left corner using FFmpeg's built-in font. Not useful on its own, but it confirms the filter works before you start layering on parameters.

Every parameter after that is a colon-separated key-value pair inside the filter string. The general pattern looks like this:

ffmpeg -i input.mp4 -vf "drawtext=text='Hello World':fontsize=48:fontcolor=white:x=100:y=100" output.mp4
Enter fullscreen mode Exit fullscreen mode

Font Control: fontfile, fontname, fontsize, fontcolor

You've got two ways to specify a font. fontfile takes a path to a .ttf or .otf file on disk. fontname takes the name of a font installed on the system and requires that FFmpeg was built with fontconfig support.

# Using a font file path
drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:text='Title'

# Using a system font name (requires fontconfig)
drawtext=fontname='Arial':text='Title'
Enter fullscreen mode Exit fullscreen mode

I almost always use fontfile because it's portable. If you're running FFmpeg in Docker or on a remote server, you can't assume system fonts are available.

fontsize accepts an integer in pixels. The default is 16, which is tiny on most video resolutions. For 1080p, I usually start around 48 and go up from there.

fontcolor accepts named colors (white, red, yellow) or hex values. Hex values in drawtext use the format 0xRRGGBB or 0xRRGGBBAA where AA is alpha. Note that this is not the CSS #RRGGBB format.

drawtext=text='Visible':fontcolor=white
drawtext=text='Semi-transparent':fontcolor=0xFFFFFF80
drawtext=text='Named color':fontcolor=yellow
Enter fullscreen mode Exit fullscreen mode

The full list of named colors comes from FFmpeg's libavutil color table, which matches the X11 color set.

Position: x and y Expressions

The x and y parameters control where the text appears, and they accept math expressions, not just static numbers. This is where drawtext gets interesting.

FFmpeg exposes several variables you can use in these expressions:

  • w and h are the video width and height
  • text_w and text_h are the rendered width and height of the text
  • n is the current frame number
  • t is the timestamp in seconds

Center the text on screen:

x=(w-text_w)/2:y=(h-text_h)/2
Enter fullscreen mode Exit fullscreen mode

Bottom-center with a margin:

x=(w-text_w)/2:y=h-text_h-40
Enter fullscreen mode Exit fullscreen mode

Top-right corner with padding:

x=w-text_w-20:y=20
Enter fullscreen mode Exit fullscreen mode

The expression (w-text_w)/2 is the standard centering formula. It takes the video width, subtracts the text width, and divides by two. Same logic applies vertically with h and text_h.

You can also animate position. Scrolling text from right to left:

x=w-t*100:y=h-text_h-20
Enter fullscreen mode Exit fullscreen mode

That moves the text 100 pixels per second toward the left edge.

Styling: box, border, and shadow

Plain white text on video is hard to read. The box, borderw, and shadow parameters fix that.

Box background draws a filled rectangle behind the text. Set box=1 to enable it, then control the color with boxcolor.

drawtext=text='Breaking News':box=1:boxcolor=black@0.6:boxborderw=10
Enter fullscreen mode Exit fullscreen mode

The @0.6 syntax sets opacity. boxborderw adds padding between the text and the box edge, in pixels. You can also specify per-side padding with boxborderw=10|20|10|20 (top, right, bottom, left).

Text border draws an outline around each character. Useful for legibility without a box.

drawtext=text='Outlined':borderw=2:bordercolor=black
Enter fullscreen mode Exit fullscreen mode

Shadow offsets a shadow copy of the text.

drawtext=text='Shadow':shadowx=3:shadowy=3:shadowcolor=black@0.5
Enter fullscreen mode Exit fullscreen mode

I combine border and shadow often. A 1px black border with a subtle shadow makes text readable on almost any background.

Dynamic Text with Expansion Expressions

Set expansion=normal (it's actually the default) to use %{...} expressions inside your text string. These evaluate per-frame.

Burn the timestamp into the video:

drawtext=text='%{pts\:hms}':fontsize=24:fontcolor=white:x=10:y=10
Enter fullscreen mode Exit fullscreen mode

The %{pts\:hms} expression renders the presentation timestamp in HH:MM:SS format. Note the backslash-escaped colon, which is necessary because colons are the parameter delimiter.

Show frame number:

drawtext=text='Frame %{n}':fontsize=20:x=10:y=10
Enter fullscreen mode Exit fullscreen mode

Show local clock time:

drawtext=text='%{localtime\:%Y-%m-%d %H\\\:%M\\\:%S}':fontsize=24:x=10:y=10
Enter fullscreen mode Exit fullscreen mode

The escaping in localtime gets ugly because strftime format codes also use colons and percent signs. Triple backslashes before colons inside the strftime pattern is the standard workaround.

You can also use the enable parameter to show text only during a specific time range:

drawtext=text='Intro':enable='between(t,0,5)':fontsize=48:x=(w-text_w)/2:y=(h-text_h)/2
Enter fullscreen mode Exit fullscreen mode

That displays "Intro" only during the first five seconds.

Text Overlays via FFmpeg Micro API

If you don't want to manage FFmpeg binaries, FFmpeg Micro runs these commands for you over HTTP.

The @text-overlay virtual option handles font loading, line wrapping, and positioning for you:

{
  "inputs": [{"url": "https://example.com/video.mp4"}],
  "outputFormat": "mp4",
  "options": [
    {
      "option": "@text-overlay",
      "argument": {
        "text": "Your text here",
        "style": {
          "charsPerLine": 18,
          "fontSize": 60,
          "lineSpacing": 15,
          "x": "0.15*w",
          "y": "(h-text_h)/2",
          "boxBorderW": 12
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

For full control, pass a raw drawtext filter string using the -vf option:

{
  "inputs": [{"url": "https://example.com/video.mp4"}],
  "outputFormat": "mp4",
  "options": [
    {
      "option": "-vf",
      "argument": "drawtext=text='Hello World':fontsize=48:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Both go to POST https://api.ffmpeg-micro.com/v1/transcodes with an Authorization: Bearer YOUR_API_KEY header.

FFmpeg Micro is a cloud API that runs FFmpeg commands over HTTP. You send a POST request with your inputs and options, and get back processed video. No binary to install, no server to manage. Get a free API key at ffmpeg-micro.com.

FAQ

How do I center text in FFmpeg drawtext?

Set x=(w-text_w)/2 to center horizontally and y=(h-text_h)/2 to center vertically. The variables w and h are the video dimensions, while text_w and text_h are the rendered size of the text string.

What's the difference between fontfile and fontname in drawtext?

fontfile takes a direct file path to a .ttf or .otf font. fontname references a font by its installed name and requires FFmpeg to be compiled with fontconfig. Use fontfile if you need consistent results across environments.

How do I add a background box behind drawtext text?

Add box=1:boxcolor=black@0.5:boxborderw=10 to your drawtext parameters. The @0.5 controls opacity (0 is fully transparent, 1 is fully opaque), and boxborderw sets the padding in pixels around the text.

Can I show a timestamp on video with drawtext?

Yes. Use text='%{pts\:hms}' with expansion=normal (the default). This renders the video's presentation timestamp in HH:MM:SS format on every frame. Escape the colon with a backslash because drawtext uses colons as delimiters.

How do I make drawtext appear only for part of the video?

Use the enable parameter with a between() expression. For example, enable='between(t,2,8)' shows the text only from the 2-second mark to the 8-second mark. The variable t is the current time in seconds.

Top comments (0)