How to style .ass subtitles for 1080x1920 vertical video
← Blog
🎬 Sous-titrage7 min read

How to style .ass subtitles for 1080x1920 vertical video

💡 To style .ass subtitles for a 1080x1920 vertical video, set PlayResX: 1080 and PlayResY: 1920 in [Script Info], then use Fontsize=68, Outline=3, Alignment=2, and MarginV=180 in [V4+ Styles]. All ASS length values scale relative to PlayRes, so those numbers only work correctly when PlayRes matches your actual frame dimensions. Burn with: ffmpeg -i input.mp4 -vf "ass=in.ass" -c:v libx264 -crf 18 -preset slow -c:a copy output.mp4.

Key takeaways

  • Set PlayResX: 1080 and PlayResY: 1920 in [Script Info] before touching any other value. ASS scales every font size, margin, and position relative to PlayRes; wrong dimensions mean mismatched, misplaced output.
  • Use Fontsize=68 to 80 in the Style line for 1080x1920. That is the range I use on creator deliverables after testing on real phone screens.
  • Set MarginV=180 to clear the like/share/comment buttons TikTok and Reels overlay at the bottom. 180px out of 1920 is roughly 9.4% from the bottom edge.
  • Use Outline=3 and BorderStyle=1. Vertical video backgrounds are busier than landscape; the heavier outline keeps text readable over any shot.
  • ASS colours use ABGR hex: white = &H00FFFFFF, black outline = &H00000000. The first two hex digits are the alpha channel, where 00 = fully opaque.

What does PlayResX and PlayResY actually control?

ASS is a resolution-independent format. Every length value in the file - font size in points, margins, and positions - is specified relative to a virtual canvas defined by PlayResX and PlayResY in [Script Info]. When libass or ffmpeg renders the file onto a real video frame, it scales all those values from the PlayRes canvas to the actual output resolution.

If your .ass file says PlayResX: 1280 and PlayResY: 720 (a common default), but your output video is 1080x1920, libass renders onto a 720px-tall virtual canvas then scales the result up to fill 1920 pixels. A MarginV=50 that looks right at 720px becomes only about 133px at 1920px - not enough to clear platform UI. Font sizes shrink proportionally.

The fix is simple: always match PlayResX and PlayResY to your video's actual output dimensions before setting font sizes or margins. For 1080x1920 vertical, that means PlayResX: 1080 and PlayResY: 1920.

The complete .ass style block for 1080x1920

Here is the full .ass file structure I start from on every vertical video project. I keep this as a template and copy it before each new job.

[Script Info]
ScriptType: v4.00+
PlayResX: 1080
PlayResY: 1920
WrapStyle: 0
ScaledBorderAndShadow: yes

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,68,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,3,0,2,20,20,180,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,First subtitle line.
Dialogue: 0,0:00:04.50,0:00:07.00,Default,,0,0,0,,Second subtitle line.

Key fields in that Style line, in the order the Format line defines them:

  • Fontsize=68: font size in ASS points, scaled relative to PlayResY. At a 1920px canvas, 68 renders at a comfortable reading size on a phone screen.
  • PrimaryColour=&H00FFFFFF: white fill, fully opaque (alpha channel 00 = no transparency).
  • OutlineColour=&H00000000: black outline. Always set this explicitly; without it libass may fall back to a grey or invisible outline.
  • Bold=0: off. At Fontsize=68 with Outline=3 the text is already heavy enough. I switch to -1 (bold on in ASS syntax) only for light-weight font faces.
  • BorderStyle=1: outline plus optional shadow. Setting it to 3 switches to an opaque background box behind the text, which I use when the shot has extreme background clutter.
  • Outline=3: 3px outline. One pixel heavier than my landscape default of 2px, because vertical video backgrounds are harder to read over.
  • Shadow=0: off. A drop shadow layered on top of a 3px outline at this font size clutters rather than helps.
  • Alignment=2: bottom-center (numpad layout: 1=BL, 2=BC, 3=BR, 4=ML, 5=MC, 6=MR, 7=TL, 8=TC, 9=TR).
  • MarginL=20, MarginR=20: 20px side margins. More important at 1080px wide (narrower canvas) than at 1920px wide landscape.
  • MarginV=180: 180px from the bottom edge of the 1920px canvas.
  • ScaledBorderAndShadow: yes (in [Script Info]): scales Outline and Shadow values consistently when the video is scaled to a different resolution later.

Which Style line fields do I change for vertical video?

When I adapt a landscape .ass template to 1080x1920, these are the specific fields I update. Everything else stays the same.

FieldLandscape 1920x1080Vertical 1080x1920Why it changes
PlayResX / PlayResY1920 / 10801080 / 1920Canvas orientation flips; all other values scale from this.
Fontsize5268-80Relative to PlayResY. Taller canvas means a larger number for the same visual size.
Outline23Vertical backgrounds are busier. Heavier outline holds text on any shot.
MarginV50180Must clear platform UI (TikTok/Reels action buttons). Landscape has no equivalent overlay on the video canvas.
MarginL / MarginR1020Frame is 1080px wide vs 1920px. The same proportional margin needs a larger pixel value on a narrower canvas.

The field order in the Style line is fixed and must match the Format: line exactly: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding. Count the commas if you are editing the line by hand.

How do I set MarginV to clear TikTok and Reels UI?

In landscape video, a MarginV=50 bottom margin comfortably clears any player chrome. Vertical 9:16 is different. TikTok, Instagram Reels, and YouTube Shorts overlay action buttons (like, comment, share, follow, and sound icon) directly on top of the video frame. Those buttons sit in a band that runs from roughly the bottom 10-20% of the frame upward.

The platforms do not publish precise pixel dimensions for these overlays in subtitle spec terms. My safe working rule: for 1080x1920, use MarginV=180 as the baseline. 180 pixels out of 1920 is about 9.4% from the bottom. On every major short-form platform I have tested, this clears the action button stack.

If the video also has prominent text or graphic elements near the bottom, I push to MarginV=220. For a cue that needs to appear at the top - when the speaker is in the lower frame and platform buttons are at the bottom - I add the inline override tag {\an8} at the start of that Dialogue line's Text field. The \an8 tag overrides the Style-line Alignment to top-center for that single cue only, while leaving all other cues at bottom-center.

Burning the .ass file into video with ffmpeg -vf ass=

Once the .ass file is styled and timed, the burn command is straightforward. The ass filter renders the file using libass, with all style already defined in the .ass itself. No force_style overrides are needed.

ffmpeg -i input.mp4 \
  -vf "ass=in.ass" \
  -c:v libx264 -crf 18 -preset slow \
  -pix_fmt yuv420p \
  -c:a copy \
  output.mp4

What each flag does:

  • -vf "ass=in.ass": render the .ass file using libass. Since PlayRes in the file matches the video dimensions, no scaling artefacts occur.
  • -c:v libx264 -crf 18 -preset slow: H.264 re-encode at visually lossless quality. Burn-in always requires a full video re-encode; -c:v copy is incompatible with video filters.
  • -pix_fmt yuv420p: 4:2:0 chroma subsampling for broad device and platform compatibility.
  • -c:a copy: copy audio unchanged. The subtitle filter does not touch the audio stream.

Before the final encode, I run a quick visual check using ffplay:

ffplay -vf "ass=in.ass" input.mp4

This renders the styled subtitles in a real-time preview window without writing a file. I use it to confirm font size and margin look right on the actual frame before committing to a full re-encode.

When should I use a soft subtitle track instead of burning in?

Burn-in is the right choice when the final delivery is a single social video and the viewer has no subtitle controls. But I use a soft track (a separate subtitle stream muxed into the container) in these cases instead:

  • Multiple language versions: one video file with Vietnamese and English subtitle tracks is far more maintainable than re-encoding the video twice.
  • Streaming platform deliveries: Netflix, Amazon, and Apple TV+ require a sidecar subtitle file, not a burned-in video. See my vertical subtitle services for both creator burn-in and streaming sidecar workflows.
  • Client review cycles: I deliver the .ass file alongside the video so the client can correct wording without triggering a full re-encode.
  • WCAG compliance: WCAG 1.2.2 (on-demand video) requires captions that viewers can disable and restyle. Burned-in text cannot be toggled, so a soft caption track is needed for formal web accessibility compliance.

When the project includes Vietnamese subtitle translation, I build the timing in the original language, translate in Subtitle Edit's dual-language view, check CPS against the Vietnamese spec limits, and deliver a soft .srt track plus a burn-in version on request.

FAQ

Can I use a font other than Arial for vertical .ass subtitles?

Yes. Set Fontname to any font installed on the machine running ffmpeg. For vertical video, I often use Noto Sans (handles Latin and Vietnamese correctly) or a bold condensed sans-serif. The font must be installed system-wide; ffmpeg cannot embed font files in a burn-in output. If the font is missing, libass silently falls back to a substitute, which may look different from what you set.

What does WrapStyle: 0 do in [Script Info]?

WrapStyle: 0 tells libass to wrap lines at word boundaries (smart wrap). It is the ASS default and almost always what you want. For vertical video I keep each subtitle event short enough (5-7 words) that wrapping rarely occurs, but WrapStyle: 0 ensures it wraps cleanly if it does. Setting it explicitly avoids relying on a renderer default that can vary.

How do I move a single cue to the top of the frame without changing the Style line?

Add the inline override tag {\an8} at the start of the Text field in that Dialogue line: Dialogue: 0,0:00:05.00,0:00:07.00,Default,,0,0,0,,{\an8}Top line here. The \an8 tag (alignment numpad 8 = top-center) overrides the Style line's Alignment for just that one cue. All other cues stay at the Style default (Alignment=2, bottom-center).

Why do my subtitles look too small even after updating PlayRes?

The most common cause: you set PlayRes correctly but forgot to raise Fontsize to match the new canvas height. A Fontsize=52 written for PlayResY=1080 will look proportionally correct at 1080px but visually too small when you switch to PlayResY=1920 without updating the value. For a 1920px canvas, start at Fontsize=68 to 80 and confirm in an ffplay preview before the full encode.

Can the same .ass file work for both landscape and vertical renders?

Not reliably. PlayRes, Fontsize, and MarginV are all different between the two orientations, and the values must match the output canvas. The cleanest workflow: maintain two template files (one at PlayResX:1920/PlayResY:1080 for landscape, one at PlayResX:1080/PlayResY:1920 for vertical). In Subtitle Edit you can keep one timing file and export it twice applying a different style header to each export.

Official Sources

Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →

DevisWhatsApp