YouTube Thumbnails
Three complete 1280×720 YouTube thumbnail layouts, each demonstrating a different composition strategy.
Layout 1 — Bold text on darkened photo
The simplest and most common YouTube format: a background photo, a dark overlay, and oversized headline text with a neon border.
from quickthumb import Canvas, Filter, Stroke, TextPart
(
Canvas.from_aspect_ratio("16:9", base_width=1280)
# Background photo, slightly darkened
.background(
image="background.jpg",
effects=[Filter(brightness=0.7)],
)
# Dark overlay to push the photo back
.background(color="#000000", opacity=0.6)
# Headline with two-tone rich text
.text(
content=[
TextPart(
text="HOW TO MAKE\nTHUMBNAILS\nIN NO TIME\n",
color="#B8FF00",
effects=[Stroke(width=8, color="#000000")],
),
TextPart(
text="Try quickthumb Today",
color="#E0E0E0",
size=48,
effects=[Stroke(width=4, color="#000000")],
),
],
size=120,
position=("8%", "50%"),
align=("left", "middle"),
bold=True,
)
# Neon green border
.outline(width=15, color="#B8FF00")
.render("youtube_01.png")
)
Key techniques:
- Two
TextPartobjects in one.text()call for headline + subtext in a single layer Strokeon both parts to maintain legibility over any backgroundoutline()as the final layer so it sits on top of everything
Layout 2 — Burnout / drama theme
A more editorial layout with a diagonal gradient, stacked text lines at different sizes and colors, and a numbered hook at the bottom.
from quickthumb import (
Background, Canvas, Filter, FitMode, LinearGradient, Shadow, Stroke,
)
(
Canvas(1280, 720)
# Background image with darkening filter
.background(
image="background.jpg",
fit=FitMode.COVER,
effects=[Filter(brightness=0.7)],
)
# Diagonal gradient overlay — dark bottom-left for text, transparent top-right
.background(
gradient=LinearGradient(
angle=120,
stops=[("#0f0f23", 0.0), ("#0f0f23cc", 0.4), ("#0f0f2300", 1.0)],
),
)
# Subheading
.text(
content="ARE YOU",
size=40,
color="#fbbf24",
position=(50, 80),
weight=900,
letter_spacing=2,
effects=[Shadow(offset_x=3, offset_y=3, color="#000000", blur_radius=0)],
)
# Headline line 1
.text(
content="BURNING",
size=150,
color="#FFFFFF",
position=(50, 130),
weight=900,
effects=[Shadow(offset_x=8, offset_y=8, color="#000000", blur_radius=0)],
)
# Headline line 2 — accent color with stroke
.text(
content="OUT?",
size=150,
color="#ff5722",
position=(50, 260),
weight=900,
effects=[
Stroke(width=3, color="#FFFFFF"),
Shadow(offset_x=8, offset_y=8, color="#000000", blur_radius=0),
],
)
# Numbered hook: "5" in an amber box
.text(
content="5",
size=70,
color="#0f0f23",
position=(50, 580),
weight=900,
effects=[
Background(color="#fbbf24", padding=(15, 25), border_radius=8),
Shadow(offset_x=4, offset_y=4, color="#000000", blur_radius=4),
],
)
.text(
content="WARNING SIGNS",
size=45,
color="#FFFFFF",
position=(150, 580),
weight=700,
effects=[Shadow(offset_x=3, offset_y=3, color="#000000", blur_radius=0)],
)
.text(
content="YOU'RE IGNORING",
size=45,
color="#fbbf24",
position=(150, 635),
weight=700,
effects=[Shadow(offset_x=3, offset_y=3, color="#000000", blur_radius=0)],
)
.render("youtube_02.png")
)
Key techniques:
LinearGradientwith three stops creates a selective dark zone without covering the whole imageBackgroundeffect on the number "5" creates an inline colored badge without a separate shape layer- Separate
.text()calls for each line gives fine-grained pixel control over vertical spacing
Layout 3 — Talking-head split
The classic "face on the right, text on the left" layout. A gradient fades the background left-to-right so the text area stays dark and the subject area stays bright.
from quickthumb import (
Canvas, Filter, FitMode, LinearGradient, Shadow, Stroke, TextPart,
)
PORTRAIT_URL = "https://images.unsplash.com/photo-1560250097-0b93528c311a?auto=format&fit=crop&w=900&q=80"
(
Canvas(1280, 720)
# Background: muted, darkened
.background(
image="background.jpg",
fit=FitMode.COVER,
effects=[Filter(brightness=0.5, saturation=0.75)],
)
# Left-to-right gradient: dark on left for text, transparent on right for subject
.background(
gradient=LinearGradient(
angle=90,
stops=[("#0A0A0A", 0.0), ("#0A0A0ACC", 0.48), ("#0A0A0A00", 1.0)],
)
)
# Topic badge
.shape(
shape="rectangle",
position=(52, 56),
width=244,
height=52,
color="#E53E3E",
border_radius=8,
effects=[Shadow(offset_x=0, offset_y=6, color="#00000066", blur_radius=10)],
)
.text(
content="MY HONEST REVIEW",
size=22,
color="#FFFFFF",
weight=900,
letter_spacing=1,
position=(174, 82),
align=("center", "middle"),
)
# Main headline: two-tone, tight line height
.text(
content=[
TextPart(text="THE TRUTH\nABOUT\n", color="#FFFFFF", weight=900),
TextPart(text="AI TOOLS", color="#FFD700", weight=900),
],
size=96,
line_height=0.95,
position=(52, 155),
align=("left", "top"),
max_width="52%",
effects=[
Stroke(width=4, color="#000000"),
Shadow(offset_x=4, offset_y=4, color="#000000", blur_radius=8),
],
)
# Sub-copy anchored near the bottom-left
.text(
content="What nobody tells you",
size=36,
color="#D4D4D4",
weight=500,
position=(52, 620),
effects=[Shadow(offset_x=2, offset_y=2, color="#000000", blur_radius=6)],
)
# Subject portrait — bottom-right, background removed
.image(
path=PORTRAIT_URL,
position=(1280, 720),
width=480,
height=680,
fit=FitMode.COVER,
align=("right", "bottom"),
remove_background=True,
effects=[Shadow(offset_x=-14, offset_y=0, color="#00000088", blur_radius=22)],
)
.outline(width=12, color="#FFD700")
.render("youtube_talking_head.png")
)
Key techniques:
align=("right", "bottom")withposition=(1280, 720)anchors the portrait to the bottom-right canvas cornerremove_background=Truecuts out the subject — requiresquickthumb[rembg]- Negative
offset_xon the portrait shadow casts it leftward, adding depth between the person and the text
Background removal
The talking-head example uses remove_background=True. Install the extra before running:
Layout 4 — Tutorial / explainer
A clean numbered-steps layout on a pure gradient background — no background photo required.
from quickthumb import Canvas, LinearGradient, Shadow, Stroke, TextPart
BLUE = "#3B82F6"
DARK = "#0F172A"
def add_step(canvas, number, label, x, y):
"""Render a numbered circle badge + step label."""
canvas.shape(
shape="ellipse",
position=(x, y),
width=72,
height=72,
color=BLUE,
align=("center", "middle"),
effects=[Shadow(offset_x=0, offset_y=6, color="#00000066", blur_radius=10)],
)
canvas.text(
content=str(number),
size=38,
color="#FFFFFF",
weight=900,
position=(x, y),
align=("center", "middle"),
)
canvas.text(
content=label,
size=36,
color="#E2E8F0",
weight=700,
position=(x + 48, y),
align=("left", "middle"),
effects=[Shadow(offset_x=2, offset_y=2, color="#000000", blur_radius=4)],
)
canvas = (
Canvas(1280, 720)
# Deep blue gradient background
.background(
gradient=LinearGradient(
angle=135,
stops=[("#0F172A", 0.0), ("#1E3A5F", 0.6), ("#0F172A", 1.0)],
)
)
# Subtle left-edge accent glow
.background(
gradient=LinearGradient(
angle=90,
stops=[(BLUE + "22", 0.0), (BLUE + "00", 0.55)],
)
)
# "HOW TO" badge
.shape(shape="rectangle", position=(52, 52), width=168, height=48, color=BLUE,
border_radius=6)
.text(content="HOW TO", size=22, color="#FFFFFF", weight=900, letter_spacing=2,
position=(136, 76), align=("center", "middle"))
# Two-tone headline
.text(
content=[
TextPart(text="MASTER\n", color="#FFFFFF", weight=900),
TextPart(text="PYTHON", color="#60A5FA", weight=900),
],
size=118,
line_height=0.95,
position=(52, 135),
align=("left", "top"),
effects=[
Stroke(width=4, color=DARK),
Shadow(offset_x=4, offset_y=4, color="#000000", blur_radius=10),
],
)
.text(content="in 30 days — from scratch", size=36, color="#94A3B8",
weight=500, position=(52, 418))
# Thin vertical divider
.shape(shape="rectangle", position=(696, 148), width=3, height=390,
color="#3B82F630", border_radius=2)
)
add_step(canvas, 1, "Learn the basics", 754, 210)
add_step(canvas, 2, "Build real projects", 754, 330)
add_step(canvas, 3, "Ship and deploy", 754, 450)
canvas.outline(width=12, color=BLUE)
canvas.render("youtube_tutorial.png")
Key techniques:
add_step()helper function keeps repeated badge+label patterns DRY while still using the standard quickthumb API- Two gradient backgrounds create a blue glow on the left edge without a visible boundary
- The
"#3B82F630"color (with30alpha) renders the divider as a subtle translucent line