What You Will Learn
- Commands for drawing text onto video with the
drawtextfilter - How to specify font, size, color, and position
- How to display timecodes and dynamic text
- How to build a scrolling ticker
Tested with: FFmpeg 6.1 (verified against real FFmpeg)
Target OS: Windows / macOS / Linux
Tested with: FFmpeg 6.1 (verified against real FFmpeg)
Basic Commands
Display Text in the Top-Left Corner
ffmpeg -i input.mp4 -vf "drawtext=text='Hello World':x=10:y=10:fontsize=36:fontcolor=white" output.mp4
Center the Text
ffmpeg -i input.mp4 \
-vf "drawtext=text='CENTER':x=(w-text_w)/2:y=(h-text_h)/2:fontsize=48:fontcolor=white" \
output.mp4
w and h are the width and height of the video, while text_w and text_h are the width and height of the rendered text. When you start combining drawtext with other filters, understanding how filtergraphs work makes complex chains far easier to write.
Key Parameters
| Parameter | Description | Example |
|---|---|---|
text | The text to display | text='Hello' |
fontfile | Path to the font file | fontfile=/path/to/font.ttf |
fontsize | Font size (pixels) | fontsize=36 |
fontcolor | Text color (name or HEX) | fontcolor=white / fontcolor=0xFFFFFF |
x / y | Position | x=10:y=10 |
shadowcolor | Shadow color | [email protected] |
shadowx / shadowy | Shadow offset | shadowx=2:shadowy=2 |
box | Background box | box=1 |
boxcolor | Background box color | [email protected] |
boxborderw | Background box padding | boxborderw=5 |
Specifying a Font
ffmpeg -i input.mp4 \
-vf "drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf:text='FFmpeg':fontsize=40:fontcolor=yellow" \
output.mp4
To display Japanese (or other CJK) text, specify a font that supports those characters.
ffmpeg -i input.mp4 \
-vf "drawtext=fontfile=/path/to/NotoSansCJKjp-Regular.otf:text='日本語テキスト':fontsize=36:fontcolor=white" \
output.mp4
Text with a Shadow
ffmpeg -i input.mp4 \
-vf "drawtext=text='Shadow Text':x=50:y=50:fontsize=40:fontcolor=white:shadowcolor=black:shadowx=3:shadowy=3" \
output.mp4
Text with a Background Box
ffmpeg -i input.mp4 \
-vf "drawtext=text='BOX TEXT':x=20:y=20:fontsize=36:fontcolor=white:box=1:[email protected]:boxborderw=8" \
output.mp4
[email protected] is a black background at 60% opacity. When the background is busy and a flat box looks heavy, you can instead apply a box blur behind the text to keep it readable.
Displaying a Timecode
ffmpeg -i input.mp4 \
-vf "drawtext=text='%{pts\\:hms}':x=w-text_w-10:y=h-text_h-10:fontsize=24:fontcolor=white:box=1:[email protected]" \
output.mp4
%{pts\\:hms} displays a timecode in HH:MM:SS.mmm format. Watch out for shell escaping (the \\: is required).
Displaying the Frame Number
ffmpeg -i input.mp4 \
-vf "drawtext=text='Frame %{n}':x=10:y=10:fontsize=24:fontcolor=yellow" \
output.mp4
Scrolling Ticker (Text Moving Right to Left)
ffmpeg -i input.mp4 \
-vf "drawtext=text='スクロールテキストのサンプルです':x=w-200*t:y=h-60:fontsize=32:fontcolor=white" \
output.mp4
For an English ticker, swap in your own text:
ffmpeg -i input.mp4 \
-vf "drawtext=text='This is a sample scrolling ticker':x=w-200*t:y=h-60:fontsize=32:fontcolor=white" \
output.mp4
With x=w-200*t, the text moves leftward as time t advances. Adjust the speed with the coefficient (200).
Overlaying Multiple Text Layers
ffmpeg -i input.mp4 \
-vf "drawtext=text='Title':x=(w-text_w)/2:y=30:fontsize=48:fontcolor=white, \
drawtext=text='Subtitle':x=(w-text_w)/2:y=90:fontsize=28:fontcolor=yellow" \
output.mp4
You can chain multiple drawtext filters with commas.
Reading from a Text File
ffmpeg -i input.mp4 \
-vf "drawtext=textfile=caption.txt:x=10:y=10:fontsize=28:fontcolor=white" \
output.mp4
Instead of text=, you can specify a text file with textfile=.
Handy Variables for Positioning
| Variable | Meaning |
|---|---|
w | Width of the video |
h | Height of the video |
text_w | Width of the rendered text |
text_h | Height of the rendered text |
t | Current time (seconds) |
n | Frame number |
Things to Watch Out For
drawtextrequires libfreetype. It is included in standard FFmpeg builds.- Multi-byte characters such as Japanese or Chinese require a font file with UTF-8 encoded glyphs.
- If the text extends beyond the video frame, it is automatically clipped (this does not cause an error).
- Be careful with escaping special characters in the shell (use
\\:for:,'\''for', and so on).
Common Pitfalls
-
Symptom: nothing is drawn at all. Cause: an empty
text, or the font cannot be found. Fix: first test with a known string such astext='Hello World', and if no font is configured, pointfontfile=at a real font file. Quote any path that contains spaces or colons with'...'. -
Symptom: a parse error when the text contains
:or'. Cause:drawtextuses a colon to separate options and quotes to wrap the whole filter, so special characters in the body are misread. Fix: escape a colon as\\:and a single quote as'\''. For longer sentences, load them from an external file withtextfile=to avoid escaping entirely. -
Symptom: Japanese or other CJK text shows as boxes (□) or blanks. Cause: the chosen font (e.g. DejaVu Sans) has no glyphs for those characters. Fix: specify a CJK-capable font such as Noto Sans CJK via
fontfile=. The font file itself must contain the glyphs you need. -
Symptom: the scrolling ticker stops partway or moves too fast. Cause: the coefficient in
x=w-200*tdoes not match your clip length or width. Fix: tune the speed coefficient between roughly100and300, and estimate the time for the text to fully clear as about(w + text_w) / coefficientseconds.
FAQ
Q. Can I use it without specifying fontfile?
A. On some builds and operating systems no default font is found and you get an error. The most reliable approach is to pass a real fontfile=. You can also select by name with font=, but the available fonts depend on the environment.
Q. How do I put a semi-transparent bar behind the text?
A. Use the box options: box=1:[email protected]:boxborderw=8. The @0.6 is the opacity (60%) and boxborderw is the padding around the text.
Q. Can I show text only for a limited time?
A. Add enable='between(t,2,5)' to draw it only between 2 and 5 seconds. Chain multiple drawtext filters with commas to show different text at different times.
Q. The timecode %{pts\\:hms} throws an error.
A. The shell is consuming the backslash. Double it as \\: as shown in the examples, or pass the filter expression through a file or script so the escaping survives.
Q. Can I keep -c:v copy after drawing text?
A. No. drawtext rewrites pixels, so the video stream is always re-encoded. You can still keep the audio untouched with -c:a copy.