Overview
MovieLite’s TextClip uses the pictex library to render beautiful, styled text overlays. This guide covers everything from simple text to complex animated titles with gradients, shadows, and custom styling.
Basic Text Overlay
Simple Text
Create a basic text overlay with default styling:
from movielite import VideoClip, TextClip, VideoWriter
from pictex import Canvas
video = VideoClip( "input.mp4" )
# Create simple text with default styling
canvas = Canvas().font_size( 60 ).color( "white" ).background_color( "transparent" )
text = TextClip( "Hello World" , start = 0 , duration = 5 , canvas = canvas)
text.set_position((video.size[ 0 ] // 2 - text.size[ 0 ] // 2 , 100 ))
writer = VideoWriter( "output_simple_text.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
TextClip automatically calculates its size based on the text content and canvas styling. Access the dimensions via text.size to position it correctly.
Styling Text with Pictex Canvas
Canvas Configuration
The Canvas object from pictex provides extensive styling options:
from pictex import Canvas, LinearGradient, Shadow
canvas = (
Canvas()
.font_family( "Arial" )
.font_size( 80 )
.color( "white" )
.padding( 30 )
.background_color(LinearGradient([ "#FF6B6B" , "#4ECDC4" ]))
.border_radius( 15 )
.text_shadows(Shadow( offset = ( 3 , 3 ), blur_radius = 5 , color = "black" ))
)
Font properties
Set font family, size, and color using .font_family(), .font_size(), and .color()
Background styling
Add solid colors or gradients with .background_color() and style with .padding() and .border_radius()
Effects
Apply shadows and other effects using .text_shadows()
Gradient Text Backgrounds
from movielite import VideoClip, TextClip, VideoWriter
from pictex import Canvas, LinearGradient, Shadow
video = VideoClip( "input.mp4" )
canvas = (
Canvas()
.font_family( "Arial" )
.font_size( 80 )
.color( "white" )
.padding( 30 )
.background_color(LinearGradient([ "#FF6B6B" , "#4ECDC4" ]))
.border_radius( 15 )
.text_shadows(Shadow( offset = ( 3 , 3 ), blur_radius = 5 , color = "black" ))
)
text = TextClip( "Styled Title" , start = 0 , duration = 5 , canvas = canvas)
text.set_position((video.size[ 0 ] // 2 - text.size[ 0 ] // 2 , 200 ))
writer = VideoWriter( "output_styled_text.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
Text Shadow Effects
Add depth to your text with shadows:
from pictex import Canvas, Shadow
# Single shadow
canvas = (
Canvas()
.font_size( 70 )
.color( "white" )
.background_color( "transparent" )
.text_shadows(Shadow( offset = ( 2 , 2 ), blur_radius = 3 , color = "black" ))
)
# Multiple shadows for a glow effect
canvas = (
Canvas()
.font_size( 70 )
.color( "white" )
.background_color( "transparent" )
.text_shadows([
Shadow( offset = ( 0 , 0 ), blur_radius = 10 , color = "#FF6B6B" ),
Shadow( offset = ( 2 , 2 ), blur_radius = 5 , color = "black" )
])
)
Text Animations
Fade In/Out
Combine TextClip with visual effects:
from movielite import VideoClip, TextClip, VideoWriter, vfx
from pictex import Canvas
video = VideoClip( "input.mp4" )
canvas = Canvas().font_size( 70 ).color( "white" ).background_color( "transparent" )
text = TextClip( "Fading Text" , start = 2 , duration = 4 , canvas = canvas)
text.set_position((video.size[ 0 ] // 2 - text.size[ 0 ] // 2 , 300 ))
# Add fade effects
text.add_effect(vfx.FadeIn( 1.0 ))
text.add_effect(vfx.FadeOut( 1.0 ))
writer = VideoWriter( "output_fade_text.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
Sliding Text
Animate position over time:
from movielite import VideoClip, TextClip, VideoWriter
from pictex import Canvas
video = VideoClip( "input.mp4" )
canvas = Canvas().font_size( 60 ).color( "white" ).padding( 20 ).background_color( "#333333" )
text = TextClip( "Sliding Text" , start = 0 , duration = 5 , canvas = canvas)
# Slide in from left over 2 seconds
def animated_position ( t ):
if t < 2.0 :
# Slide in from left
x = int ( - text.size[ 0 ] + (text.size[ 0 ] + video.size[ 0 ] // 2 ) * (t / 2.0 ))
else :
# Stay centered
x = video.size[ 0 ] // 2 - text.size[ 0 ] // 2
y = video.size[ 1 ] // 2 - text.size[ 1 ] // 2
return (x, y)
text.set_position(animated_position)
writer = VideoWriter( "output_animated_pos.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
Bouncing Text
Create physics-inspired animations:
import math
from movielite import VideoClip, TextClip, VideoWriter
from pictex import Canvas
video = VideoClip( "input.mp4" )
canvas = Canvas().font_size( 80 ).color( "#FFD700" ).background_color( "transparent" )
text = TextClip( "Bounce!" , start = 0 , duration = 6 , canvas = canvas)
# Bounce effect with decreasing amplitude
def bounce_position ( t ):
x = video.size[ 0 ] // 2 - text.size[ 0 ] // 2
# Bounce amplitude decreases over time
amplitude = 200 * ( 1.0 - t / 6.0 )
frequency = 3.0
y = int (video.size[ 1 ] // 2 - text.size[ 1 ] // 2 -
abs (math.sin(t * frequency * math.pi)) * amplitude)
return (x, y)
text.set_position(bounce_position)
writer = VideoWriter( "output_bounce.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
Zoom Text
Scale text dynamically:
from movielite import VideoClip, TextClip, VideoWriter
from pictex import Canvas
video = VideoClip( "input.mp4" )
canvas = Canvas().font_size( 70 ).color( "white" ).background_color( "transparent" )
text = TextClip( "ZOOM IN" , start = 0 , duration = 3 , canvas = canvas)
text.set_position((
video.size[ 0 ] // 2 - text.size[ 0 ] // 2 ,
video.size[ 1 ] // 2 - text.size[ 1 ] // 2
))
# Zoom from 0.5x to 1.5x over 3 seconds
text.set_scale( lambda t : 0.5 + ( 1.0 * (t / 3.0 )))
writer = VideoWriter( "output_zoom_text.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, text])
writer.write()
video.close()
Multiple Text Layers
Titles and Subtitles
Layer multiple text elements with different timings:
from movielite import VideoClip, TextClip, VideoWriter, vfx
from pictex import Canvas, LinearGradient
video = VideoClip( "input.mp4" )
# Title (appears first)
canvas1 = Canvas().font_size( 90 ).color( "white" ).background_color(
LinearGradient([ "#FF6B6B" , "#FF8E53" ])
)
title = TextClip( "Title" , start = 0 , duration = 3 , canvas = canvas1)
title.set_position((video.size[ 0 ] // 2 - title.size[ 0 ] // 2 , 100 ))
title.add_effect(vfx.FadeIn( 0.5 ))
title.add_effect(vfx.FadeOut( 0.5 ))
# Subtitle (appears after title)
canvas2 = Canvas().font_size( 50 ).color( "#CCCCCC" ).background_color( "transparent" )
subtitle = TextClip( "Subtitle text here" , start = 1 , duration = 4 , canvas = canvas2)
subtitle.set_position((video.size[ 0 ] // 2 - subtitle.size[ 0 ] // 2 , 220 ))
subtitle.add_effect(vfx.FadeIn( 0.5 ))
subtitle.add_effect(vfx.FadeOut( 0.5 ))
# Caption (appears last)
canvas3 = Canvas().font_size( 40 ).color( "yellow" ).background_color( "transparent" )
caption = TextClip( "Additional info" , start = 2.5 , duration = 3 , canvas = canvas3)
caption.set_position((video.size[ 0 ] // 2 - caption.size[ 0 ] // 2 , video.size[ 1 ] - 100 ))
caption.add_effect(vfx.FadeIn( 0.3 ))
caption.add_effect(vfx.FadeOut( 0.3 ))
writer = VideoWriter( "output_multi_text.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, title, subtitle, caption])
writer.write()
video.close()
Professional Templates
Lower Third
Create professional name/title overlays:
from movielite import VideoClip, ImageClip, TextClip, VideoWriter, vfx
from pictex import Canvas
video = VideoClip( "input.mp4" )
# Background bar
bar = ImageClip.from_color(
color = ( 0 , 0 , 0 , 200 ), # Semi-transparent black
size = (video.size[ 0 ], 120 ),
duration = 5
)
bar.set_position(( 0 , video.size[ 1 ] - 120 ))
bar.add_effect(vfx.FadeIn( 0.5 ))
bar.add_effect(vfx.FadeOut( 0.5 ))
# Name text
canvas_name = Canvas().font_size( 50 ).color( "white" ).background_color( "transparent" )
name = TextClip( "John Doe" , start = 0 , duration = 5 , canvas = canvas_name)
name.set_position(( 40 , video.size[ 1 ] - 100 ))
name.add_effect(vfx.FadeIn( 0.5 ))
name.add_effect(vfx.FadeOut( 0.5 ))
# Title text
canvas_title = Canvas().font_size( 30 ).color( "#AAAAAA" ).background_color( "transparent" )
job_title = TextClip( "Software Engineer" , start = 0 , duration = 5 , canvas = canvas_title)
job_title.set_position(( 40 , video.size[ 1 ] - 50 ))
job_title.add_effect(vfx.FadeIn( 0.5 ))
job_title.add_effect(vfx.FadeOut( 0.5 ))
writer = VideoWriter( "output_lower_third.mp4" , fps = video.fps, size = video.size)
writer.add_clips([video, bar, name, job_title])
writer.write()
video.close()
Lower thirds are most effective when they appear briefly (3-5 seconds) and use subtle fade in/out animations.
Advanced Gradient Backgrounds
Full Screen Gradient with Text
from movielite import ImageClip, TextClip, VideoWriter
from pictex import Canvas, LinearGradient, Column, Text
WIDTH = 1920
HEIGHT = 1080
# Create gradient background
canvas_bg = (
Canvas()
.background_color(
LinearGradient(
colors = [ "#ff0000" , "#0000ff" ],
start_point = ( 0.0 , 0.0 ), # top-left corner
end_point = ( 1.0 , 1.0 ) # bottom-right corner
)
)
.size( WIDTH , HEIGHT )
)
background_clip = (
ImageClip(canvas_bg.render().to_numpy())
.set_duration( 10 )
)
# Add text overlay
canvas_text = Canvas().font_size( 80 ).color( "white" ).background_color( "transparent" )
text = TextClip( "Beautiful Gradient" , start = 0 , duration = 10 , canvas = canvas_text)
text.set_position(( WIDTH // 2 - text.size[ 0 ] // 2 , HEIGHT // 2 - text.size[ 1 ] // 2 ))
writer = VideoWriter( "output.mp4" , fps = 30 )
writer.add_clip(background_clip)
writer.add_clip(text)
writer.write()
Pictex Layouts
Use Pictex’s layout components for complex text arrangements:
from movielite import ImageClip, VideoWriter
from pictex import Canvas, Column, Text
WIDTH = 1920
HEIGHT = 1080
# Create background canvas
canvas = (
Canvas()
.background_color( "white" )
.size( WIDTH , HEIGHT )
)
# Create column with multiple text elements
column = (
Column(
Text( "Hello, world!" ).font_size( 50 ).color( "blue" ),
Text( "This will be the bottom text." ).font_size( 30 ).color( "green" )
)
.gap( 20 )
.align_items( "center" )
.background_color( "lightgray" )
.place( "center" , "center" )
)
# Render to ImageClip
background_clip = (
ImageClip(canvas.render(column).to_numpy())
.set_duration( 10 )
)
writer = VideoWriter( "output.mp4" , fps = 30 )
writer.add_clip(background_clip)
writer.write()
Centering Text
Horizontal Centering
# Center horizontally
text.set_position((video.size[ 0 ] // 2 - text.size[ 0 ] // 2 , 100 ))
Vertical Centering
# Center vertically
text.set_position(( 100 , video.size[ 1 ] // 2 - text.size[ 1 ] // 2 ))
Center Both Axes
# Center both horizontally and vertically
text.set_position((
video.size[ 0 ] // 2 - text.size[ 0 ] // 2 ,
video.size[ 1 ] // 2 - text.size[ 1 ] // 2
))
Best Practices
Readability Use high contrast between text and background. White text on dark backgrounds (or vice versa) works best. Add shadows for better separation.
Font Size For 1080p videos, use font sizes between 40-80px for body text and 80-120px for titles. Test on the target display size.
Duration Give viewers enough time to read text. A rule of thumb is 3 seconds minimum, plus 1 second per 10 words.
Animation Timing Keep fade in/out animations short (0.3-0.5s). Longer animations can feel sluggish.
Common Patterns
Simple Caption
Title Card
Watermark
canvas = Canvas().font_size( 40 ).color( "white" ).background_color( "transparent" )
text = TextClip( "Caption" , start = 0 , duration = 3 , canvas = canvas)
text.set_position(( 50 , video.size[ 1 ] - 100 ))
Next Steps