Shorts / Vertical Cover
A 1080×1920 vertical cover built entirely from a JSON spec — no Python composition code. This demonstrates the JSON-first workflow where a spec file (written by hand or generated by an AI agent) is loaded and rendered in one call.
How it works
from quickthumb import Canvas
with open("shorts_cover.json", encoding="utf-8") as f:
json_spec = f.read()
Canvas.from_json(json_spec).render("shorts_cover.png")
That's it. The entire composition lives in the JSON file.
The JSON spec
{
"width": 1080,
"height": 1920,
"layers": [
{
"type": "background",
"image": "background.jpg",
"fit": "cover",
"effects": [
{ "type": "filter", "brightness": 0.34, "contrast": 1.18, "saturation": 0.72, "blur": 4 }
]
},
{
"type": "background",
"gradient": {
"type": "linear",
"angle": 18,
"stops": [
["#020617E6", 0.0],
["#020617CC", 0.42],
["#02061799", 0.72],
["#020617F7", 1.0]
]
}
},
{
"type": "shape",
"shape": "ellipse",
"position": ["86%", "25%"],
"width": 400,
"height": 400,
"color": "#1D4ED8",
"align": "center",
"opacity": 0.32,
"effects": [{ "type": "glow", "color": "#1D4ED8", "radius": 46, "opacity": 0.45 }]
},
{
"type": "shape",
"shape": "rectangle",
"position": ["8%", "8%"],
"width": 334,
"height": 92,
"color": "#C1121F",
"border_radius": 18,
"rotation": -3,
"align": "top-left",
"effects": [
{ "type": "shadow", "offset_x": 0, "offset_y": 10, "color": "#000000", "blur_radius": 16 },
{ "type": "stroke", "width": 2, "color": "#FCA5A5" }
]
},
{
"type": "text",
"content": "JSON-FIRST",
"size": 36,
"color": "#FFFFFF",
"position": ["23.4%", "10.4%"],
"align": "center",
"weight": 900,
"letter_spacing": 2,
"effects": [{ "type": "shadow", "offset_x": 2, "offset_y": 2, "color": "#000000", "blur_radius": 4 }]
},
{
"type": "text",
"content": [
{ "text": "TURN LONG\nVIDEOS INTO", "color": "#F8FAFC", "weight": 900, "effects": [] },
{
"text": "\nSHORTS",
"color": "#A3FF12",
"weight": 900,
"effects": [{ "type": "stroke", "width": 6, "color": "#000000" }]
}
],
"size": 128,
"position": ["8%", "26%"],
"align": "left",
"max_width": "52%",
"line_height": 1.0,
"letter_spacing": -1,
"effects": [{ "type": "shadow", "offset_x": 8, "offset_y": 8, "color": "#000000", "blur_radius": 12 }]
},
{
"type": "shape",
"shape": "rectangle",
"position": ["80.5%", "42.3%"],
"width": 340,
"height": 720,
"color": "#020617",
"border_radius": 44,
"opacity": 0.98,
"rotation": 8,
"align": "center",
"effects": [
{ "type": "stroke", "width": 3, "color": "#CBD5E1" },
{ "type": "shadow", "offset_x": 0, "offset_y": 22, "color": "#000000", "blur_radius": 28 }
]
},
{
"type": "image",
"path": "preview.jpg",
"position": ["80.5%", "42.3%"],
"width": 286,
"height": 596,
"fit": "cover",
"rotation": 8,
"align": "center",
"border_radius": 28,
"effects": [{ "type": "filter", "brightness": 0.86, "contrast": 1.12, "saturation": 0.96 }]
},
{
"type": "text",
"content": "JSON in.\nCover out.",
"size": 58,
"color": "#E2E8F0",
"position": ["8%", "66.5%"],
"align": "left",
"max_width": "42%",
"auto_scale": true,
"line_height": 1.1,
"effects": [
{ "type": "background", "color": "#0F172AE6", "padding": [20, 24], "border_radius": 16 },
{ "type": "shadow", "offset_x": 0, "offset_y": 10, "color": "#000000", "blur_radius": 20 }
]
},
{
"type": "text",
"content": [
{ "text": "AGENT EMITS JSON", "color": "#FBBF24", "weight": 800, "effects": [] },
{ "text": " | QUICKTHUMB RENDERS", "color": "#94A3B8", "weight": 500, "effects": [] }
],
"size": 36,
"position": ["8%", "88.8%"],
"align": "left",
"max_width": "44%",
"effects": [{ "type": "shadow", "offset_x": 2, "offset_y": 2, "color": "#000000", "blur_radius": 4 }]
},
{
"type": "outline",
"width": 6,
"color": "#0F172A"
}
]
}
Key techniques
Rotated phone mockup
The "phone frame" is two layers at the same position and angle — a dark rectangle with a stroke (the frame), and an image with matching rotation (the screen content). Both use "rotation": 8 and "align": "center" at ["80.5%", "42.3%"].
{ "type": "shape", "shape": "rectangle", "rotation": 8, "border_radius": 44, ... }
{ "type": "image", "rotation": 8, "border_radius": 28, ... }
The shape's border_radius is slightly larger than the image's, creating a visible border gap that acts as the phone bezel.
Decorative glow ellipse
A semi-transparent blue ellipse with a glow effect sits at ["86%", "25%"] — visually balancing the right side of the composition even though it has no text content.
{
"type": "shape",
"shape": "ellipse",
"color": "#1D4ED8",
"opacity": 0.32,
"effects": [{ "type": "glow", "color": "#1D4ED8", "radius": 46, "opacity": 0.45 }]
}
auto_scale for safe text fitting
The caption box uses "auto_scale": true with "max_width": "42%" to shrink the font if the text grows — useful when this spec is generated by an AI that might write longer copy than expected.
Slight negative letter_spacing
The main headline uses "letter_spacing": -1 to tighten the very large (128px) text and prevent awkward gaps between characters at display sizes.
Why JSON-first?
- The spec is a plain text file — easy to version-control, diff, and review
- Any process (script, API, AI agent) can produce or modify it
Canvas.from_json()validates the spec before rendering — bad input raisesValidationErrorimmediately- The composition can be re-rendered any time by just running
Canvas.from_json(spec).render(...)
See JSON Schema & AI Workflow for the complete schema reference.