Clean thumbnail
The one rule
Real images carry the mood; the type stays quiet. Every thumbnail is a small stack of real images in fixed roles plus two pieces of type. No outlines, glows, arrows, stickers, color washes, or scenes generated by an image model.
Image roles
Decide each image's role from what the user says and shows. There are only four:
| Role | Flag | Use for |
|---|---|---|
| Background | the capture (positional) | Almost everything: one frame, photo or key art |
| Panels | --panel A --panel B (2-3), --panel-label each | Comparisons: versions, settings, before/after |
| Subject | --subject cutout.png (repeatable) | A person, character or product in front of the background |
| Inset | --inset img --inset-box x0,y0,x1,y1 | An image shown inside the background: a game on a monitor, a clip on a phone |
Panels replace the background; subject and inset sit on top of one. Subjects go on the side away
from the type and are matched to the photo behind them (color cast, tone, sharpness, grain), and
the background is softened behind them (--push, default 0.5). Subject files need a transparent
background; if they don't have one, the script uses rembg when installed, else asks for a cut-out.
The inset box is in fractions of the background image: look at the background and estimate where
the screen or frame is.
Thumbnail or post?
Decide this first; it changes what happens to the user's words.
- Thumbnail: the image sits next to a video title (a YouTube video, a Reel or Short cover). The title carries the message, so the image gets a few words, picked with the type rules.
- Post: the image is the message: a Facebook or Instagram post, an announcement, an event, an
offer, a hiring post, a promo. Render with
--mode postand keep the user's words: the core message as--title, the rest as--body, when, where, price and how to act as--details(type rules: Posts). Fix spelling and capitals only. Never cut the message or move it into the post's caption unless the user asks; if the script says the text won't read at feed size, show the user the cut you would make and wait for a yes. Posts often read best with--backdrop solidwhen the photo is busy or the message is long; thumbnails usually keep the defaultfade. - Unsure? Ask once: "Is this a video thumbnail or a post image?"
Workflow
-
Get the images. The best background is a raw capture: photo mode, HUD off, 1440p or 4K, subject on one side with room for type on the other. Official press-kit art works; key art often has the subject in the middle, which
--subject-boxand--crophandle (step 5). Fan art needs the artist's permission and credit; say so if an image looks like fan art. Don't generate or repaint scenes with an image model. -
Write the text with references/type-rules.md. Thumbnail: the big type is what makes this video different, the label is context. No kicker unless it is needed. Show the user the text and what you dropped from the title. Post: arrange the user's message into headline, body and details without dropping words, and show them how you split it.
-
Pick the title look with references/fonts.md, in this order: the official logo if the user has one (
--logo, press kits and official sites publish them); otherwise the bundled font closest to the game's, film's or brand's own lettering (--title-font cinzel, see--list-fonts); if nothing bundled fits, don't force it: use the channel's savedtitle_font, else the serif or sans default, and say that nothing matched. The label box always keeps the channel font. Tell the user which look you used and why, and that a matched font is close in spirit, not the real lettering. Match the logo's weight and spacing as well as its shape: a thin, widely spaced logo gets a light weight and wide tracking (--title-font outfit@Light --title-tracking 0.12for Death Stranding), not the catalog's bold default. Split long single words over two lines (DAWN\nWALKER) when the font is wide; the script warns when the type comes out small. Color: white by default. When the image has one strong signature color (a sunset, neon, an eclipse, a team color), use--title-color auto: the script takes that color from the image and lightens it just enough to read on the dark side (it stays white if the image has no vivid color). Or give a brand color (--title-color "#f2b84b"). Color the big type or the label box (--accent), never both in different colors; the script warns if you do. Save a channel color astitle_colororaccentinclean-thumbnail.json. -
Check for channel settings and formats. If
clean-thumbnail.jsonis in the working folder, the script loads it (style, fonts, colors, lead, size, aspect and so on; flags override it).- Formats, asked once. If there is no saved
aspectand the request doesn't name a platform, ask once which platforms the user posts to: YouTube (16:9), Facebook or Instagram feed (4:5), Reels, Shorts or Stories (9:16). Save the answer as"aspect"inclean-thumbnail.json(create it from references/channel.example.json if missing, e.g."aspect": "16:9,4:5"), say where you saved it, and don't ask again. - After that, use the saved formats without asking. If a request names a platform ("for
Reels", "a Facebook one too"), pass
--aspectfor that request only; change the saved value only when the user says so ("from now on", "always"). - After the user approves a first thumbnail, offer to save their other choices (style, fonts,
title_colororaccent) the same way. Save a color only when the user wants it on every video;autois decided per image, so leave it unsaved unless they ask.
- Formats, asked once. If there is no saved
-
Render with
scripts/thumb.pyin this skill's folder (use its full path):python <skill-dir>/scripts/thumb.py CAPTURE --title "SIMON" --label "No hit · Parry only" -o out/simonWith the default
--lead abthis writes two variants,_A(title big) and_B(label big), each as a master.jpgat the largest standard size the images support without upscaling (up to 3840x2160, for desktop upload), a_phone.jpgat 1280x720 under 2 MB (for phone upload), and a_preview.png(feed sizes with the duration badge, 88 px notification, squint test).Formats:
--aspect 16:9(default, YouTube),4:5(Facebook and Instagram feed, 1080x1350),9:16(Reels, Shorts, Stories, 1080x1920), a comma list, orall. Vertical formats crop to the busiest part of the image (the subject) unless--focusis given, and put the type in a band below the image, above it when there is a subject, or on the seam between stacked panels. In 9:16 the type stays inside the Reels/Shorts safe zone (clear of the top 14%, bottom 35% and right 15%). Each vertical file gets a_1080.jpgcopy when the master is larger, and a preview in a Facebook feed card or under a Reels overlay with the profile-grid crop. Use the same text for every format.Mark the subject. When the capture shows a person, character or product, look at it and pass
--subject-box x0,y0,x1,y1around the face (or the product's name or label), in fractions of the image as given, the same way you estimate--inset-box. Box the face from the brows to the chin, not the hair and not the whole body: type crossing a torso is fine, type or shade on a face is not. The script then puts the 16:9 type on the side that keeps the box clear, centers the 4:5 and 9:16 crops on it, moves the 9:16 type band to the top when that shades the face less, and warns when the box still ends up under type or shade or out of frame. Each variant prints asubject box:line (in frame, visible, under shade, under type). Not with--panelor--subject(a cut-out has its own overlap check).A different picture for 4:5 and 9:16 is often better. The 9:16 type sits mid-frame (Reels safe zone), so it needs a frame with the subject's face in the top third and something calm below it. With a centered face,
--subject-boxmoves the band to the top when that clears it and warns when it can't; without a box the band stays at the bottom and shades it. When the wide capture doesn't suit, ask for (or pick from the user's captures) one that does and render it in a second run with--aspect 4:5,9:16(the same-ois fine: vertical files get_4x5and_9x16suffixes, so the 16:9 files stay). The vertical crop centers on--subject-box; without one it follows the busiest part of the frame, which is sometimes scenery: then pass a box or--focus.Subject in the middle? In a 16:9 thumbnail the type and its shade then land on the subject whichever side they take, and
--focuscan't help (it only moves the 16:9 crop on captures wider than 16:9). With--subject-box, the warning prints a crop that clears it, e.g.try --crop 0.11,0,0.96,1 --text-side right: re-run with it and keep the same--subject-box(both take fractions of the image as given, so the box needs no recalculating). By hand: keep a narrower part of the image with--crop x0,y0,x1,y1(fractions of the image as given): about 75-80% of the width, with the subject toward the side away from the type. Narrower than 16:9, the art moves to the far side and fades into the dark side. The crop applies to every format; bars are still trimmed. A crop can also keep a second element in view that the box doesn't know about (the creator demo keeps the camera phone visible this way).Other flags:
--backdrop fade|solid(what sits behind the type: fade darkens the photo into a shade, solid keeps the photo clean and puts the type on a solid--bgpanel),--style serif|sans(default title look),--kicker TEXT/--subtitle TEXT(small lines above or below the big type; see the type rules),--feather 0..1(fade length of narrow or cropped art; shorten it when the fade reaches a face),--shade 0..1and--bg #hex(darkness and color of the type side),--subject-box x0,y0,x1,y1,--band auto|top|bottom(type band on 4:5 and 9:16),--text-side left|right,--focus 0..1(crop position on wide captures),--logo logo.png --logo-white(logo as the title),--title-font NAME|FILE/--label-font,--list-fonts,--system-fonts(installed faces instead of the bundled defaults; the look then varies by machine),--title-tracking 0..0.5(big type letter spacing in em),--title-color white|auto|#hex(big type) /--accent white|auto|#hex(label box),--size 720p|1080p|1440p|4k,--feed DIR(place both variants among other thumbnails). Each variant printstitle type: ...andcolors: ..., so you can tell the user what was used. -
Check before handing back:
- Read every
warning:line and fix the cause (switch side, move the crop or inset box, shorten the text, use a bigger cut-out, recapture). Never hand back a thumbnail with an open warning without saying so. - Open each preview: the big type must read at 168 px. If it doesn't, shorten it.
- In the squint tile, the big type and the subject should be the two things that survive. If a bright patch outweighs both, change the crop, the side or the image. A thin or detailed logo often fades here while big type holds; say so, since that is what variant B is for.
- Read the
subject box:line of every variant. If the box is shaded or under type on 16:9, re-run with the--cropthe warning suggests (step 5) instead of shrinking the type. On 9:16 the type must stay inside the Reels safe zone, so the script can only move the band to the top or bottom (it does so itself, and the warning names--bandwhen the other band clears the face): if the face is still shaded, check the box runs from the brows to the chin, shorten the text, or use another frame for the vertical formats (step 5), and tell the user if the 9:16 face stays shaded. - Without a box (no person or product), check in the 4:5 and 9:16 previews that the crop kept
what matters; if it framed scenery, pass
--focus. - With
autocolor, check thecolors:line and the preview: the color should be the one the image is known for. If it took a side color (a red sign in a blue scene), give the hex code of the right one or go back to white. - With a subject, check that it looks photographed with the background: no halo, no mismatched color or sharpness. The script prints what it matched.
- If the user has screenshots of current top thumbnails on the topic, pass them with
--feed DIRand look at_feed.png: theirs should stand out.
- Read every
-
Hand back. A thumbnail: both variants (master and phone copy), the previews, the text used, and the title look and colors (from the
title type:andcolors:lines). The pair is for YouTube's Test & Compare, which takes up to three thumbnails per video and picks the winner on watch time share, not click-through rate; clearly different thumbnails finish faster. Let the test pick, not you, and don't promise a third variant. A post: the image, its preview and the text exactly as placed, with the sizes from thepost text:line; it is one image, not an A/B pair.
Fixed specs
Master up to 3840x2160 (YouTube accepts up to 50 MB from desktop); phone copy 1280x720 under 2 MB.
4:5 master up to 2160x2700 with a 1080x1350 copy; 9:16 up to 2160x3840 with a 1080x1920 copy.
Type stays out of the bottom-right corner, where the duration badge sits (sized to the badge at
168 px), inside the Reels/Shorts
safe zone on 9:16, and inside the frame everywhere; it shrinks to fit the height and width, and
warns if it still can't. Every element (title or hook lines, kicker, subtitle, label box, logo,
panel labels) needs WCAG 4.5:1 contrast against what is behind it; the script darkens the type
area once when light type fails because of the background, and names the element and the fix when
it can't (a brighter background, or a --title-color or --accent too dark to read). auto
colors are lightened until they pass; a hand-picked color is never changed, only flagged.
Inputs are checked rather than guessed: a missing named font, an opaque logo, a fully transparent
cut-out, --inset/--subject/--crop/--subject-box with --panel, --subject-box with
--subject, a --crop that leaves nothing, bad channel settings and an -o that would overwrite an
input all stop with a clear message. Letterbox and pillarbox bars (including video black 16 and
bars carrying subtitles) are trimmed; dark scene edges such as a night sky are kept.