Hand-interactive titles
Type that the person on camera physically touches: pressed flat by a palm, shoved off
screen, lifted, swatted. Everything is local and free: hand tracking and the hand matte
are numpy/Pillow, the titles are PNGs animated by box/x/scale keyframes in an
hn4a-compositor spec (format and commands in hn4a-compositor-motion-graphics).
1. Cut and find the gestures
- Trim to the take first (frame-accurate re-encode:
-ss A -to B -c:v libx264 -crf 16). - 2 fps contact sheet of the whole source to find takes, then a 6 fps sheet of the chosen one to find each gesture's first and last frame. Write the cue frames down.
- One gesture per title. A push-down needs a few frames of the hand held still at the top so the title can land before the push starts.
2. Track the hand (free)
Pull every frame at 640 px wide (ffmpeg -vf scale=640:-1 f_%03d.jpg) and threshold skin:
skin=(R-G>28)&(R>110)&(R-B>35)&(G>60)
- Mask out anything skin-coloured that isn't the hand (posters, wood, the face) by rectangle, and limit to the side of frame the hand works in.
- Per frame, per hand region: use percentiles, not min/max (98th pct of y for a palm's lower edge, 99th pct of x for a palm's leading edge, 2nd-98th pct of x for its centre).
- Smooth: 5-frame moving average on the edge, 9 on the centre, and
np.maximum.accumulatefor a push that only goes one way. - Contact sheet with the numbers drawn on before trusting them. Lighting changes and a camera reframe mid-take break the threshold: re-check after any reframe.
3. Hand matte, so the hand stays in front
Full-res frames for the span the title is near the hand, the same skin mask,
MedianFilter(5), MaxFilter(5), GaussianBlur(1.5), put on the frame as alpha, then
ffmpeg -framerate 30 -i hand/f_%03d.png -c:v libvpx-vp9 -pix_fmt yuva420p -crf 18 -auto-alt-ref 0 -g 6 hand_cutout.webm.
Layer order: footage, title, hand cutout (same start, trim 0, fit: cover). Look at a
masked frame on magenta before using it. Pay for birefnet-matte only if the skin mask
fails (sleeves the colour of skin, busy backgrounds).
4. Titles as PNGs
Render each title to a transparent PNG at 3x (Pillow), cropped to its content, and give
the image layer fit: fill so box keyframes stretch it: that is what makes a squash.
Equal-width lines: render each line, resize to the same width, stack.
5. Motion
Squash under a push-down. Per tracked frame: top sits just below the palm
(top = hand_bottom + gap*(1-p)**0.5, gap ~28 px at 1080p, closing to contact as it
flattens), height falls from its natural height to ~22 px, width grows a little as it
flattens (squash and stretch, w = W*(1+0.32*q**2), so the flat strip shows past the
palm). Let the squash run ahead of the hand: q = min(1, p/0.6) flattens it with 40% of
the push left. Linear keys, one per frame. Pop in with scale 0.88 to 1.03 to 1 over
0.24 s as the hand settles at the top; fade out over ~0.12 s the frame the hand lands.
Shove off. Place the title just past the palm's leading edge before the push. From
the contact frame: x keys that keep the title ahead of the palm (match the palm's
measured speed for the first 2-4 frames), then in ease off screen (~1400 px over
0.34 s), with rotate to ~12-16 deg, scale to ~1.3, y lifting ~100 px, blur to ~8 px.
transformOrigin at the side being pushed.
6. Look
What tends to land, and what doesn't:
- One bold typeface (a heavy display weight, tight caps), one flat colour, one solid hard offset shadow in a brand accent. Small kicker line above a big line, same width.
- Don't stack effects (textured display fonts plus outlines plus offset strokes reads as busy), don't put titles in boxes or cards, avoid stretched type and thick sticker outlines, and avoid plain clean type with no treatment (reads as boring).
- Take the palette from the brand's site, not just its logo folder: read computed styles in the browser for fonts and colours.
- Show two looks at most, on the real footage.
7. Review and render
- Stills at landing, mid-squash, flat, before the shove, at contact and mid-flight: check the title isn't covering the hand and the shove keeps the title ahead of the palm.
- Put the specs on a review page as
{"compose": ...}candidates, and also send the rendered MP4s into the chat: the person may not have the browser pane visible. - Render with
hn4a-compositor render; check frame count and a contact sheet. - If hn4a goes offline mid-review, the review page's live players stop; render a preview another way and say so.
Related
python-synth-sfx-for-motion-graphics for pops, squashes and whooshes cued to these
moves.


