Communitygithub.com

amrodwaire/phone-reel-edit

Editing playbook for turning phone footage into vertical Reels, Shorts or TikToks with Arabic on-screen text. Covers checking the source, cutting, stabilising and slow motion with ffmpeg, colour correction by measured numbers (an ffmpeg LUT, or DaVinci Resolve free), Arabic text in Remotion with an automatic overlap, safe-zone and contrast check, cutting music to the edits, loudness mastering for phone speakers, and checks before publishing. Use it whenever a vertical video is cut, re-cut, grade

What is phone-reel-edit?

phone-reel-edit is a Claude Code agent skill that editing playbook for turning phone footage into vertical Reels, Shorts or TikToks with Arabic on-screen text. Covers checking the source, cutting, stabilising and slow motion with ffmpeg, colour correction by measured numbers (an ffmpeg LUT, or DaVinci Resolve free), Arabic text in Remotion with an automatic overlap, safe-zone and contrast check, cutting music to the edits, loudness mastering for phone speakers, and checks before publishing. Use it whenever a vertical video is cut, re-cut, grade.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/amrodwaire/phone-reel-edit/tree/HEAD

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Phone reel editing

Most rules here come from a real reel that failed at that step first. references/research.md holds the reasons and sources. references/resolve-ui.md maps the DaVinci Resolve controls for driving it with computer use.

Some cases have not been through a published reel yet: HDR sources, 30 fps slow motion, speech on camera, TikTok and Shorts safe boxes, and covers. Their commands were run on test clips only. references/other-cases.md holds them and says how each was checked. Tell the user when an edit relies on one of them.

Pipeline: phone source, ffmpeg cut and stabilise, colour (DaVinci Resolve, or scripts/grade.py for whole-frame moves), Remotion text, ffmpeg sound and master.

Before cutting

  1. Read the brand rules for the client, if there are any: banned words, contact line, AI labelling, and what needs approval before posting.
  2. Check every source clip: python -I scripts/probe_source.py clip.mp4. It reports rotation, frame rate and whether it varies, SDR or HDR, colour tags and audio, then lists what the clip needs before cutting. Typical phone clips are SDR bt709 at about 30 or 60 fps. HDR, variable frame rate and 30 fps sources each need an extra step, in references/other-cases.md. Background talk means the original sound stays muted. If the person on camera is speaking to the viewer, see the speech section of the same file.
  3. If you use a second AI model as a reviewer, discuss the idea with it before deciding, then verify what it says. references/research.md lists the kinds of mistakes a reviewer model made.

Pace

  • One idea per reel. The text sets the length, not a target number. A test reel needed 18.6 s because the client could not finish reading at 10.5 s or 13.5 s.
  • Budget 10 Arabic characters a second, counted from when a line appears. That is slow on purpose: the viewer is also watching the picture on a phone.
  • Two reveals per shot where possible, and at least 1.5 s between them. Lines that appear together count as one reveal.
  • A native reader's verdict on pace outranks any model's.
  • The strongest visual and the hook text are on screen at frame 0, with no fade from black.
  • Avoid a sudden 3x speed-up. Ramp gently through a bad moment instead.

60 fps played at 0.5x gives real frames at 30 fps, so no interpolation is needed. A 30 fps source has no spare frames, so read the slow-motion section of references/other-cases.md before slowing one. Stabilise to a 60 fps intermediate, then retime:

ffmpeg -ss S -t D -i raw.mp4 -vf "vidstabdetect=shakiness=6:accuracy=15:result=x.trf" -f null -
ffmpeg -ss S -t D -i raw.mp4 -vf "vidstabtransform=input=x.trf:smoothing=14:zoom=2:optzoom=0:interpol=bicubic,crop=W:H:X:Y,scale=1080:1920:flags=lanczos,fps=60" -an -c:v libx264 -preset veryfast -crf 10 -pix_fmt yuv420p im.mp4
python -I scripts/retime.py im.mp4 c1.mp4 --start 0.4 --frames 99 --keys 0:.5

Run scripts/ paths from this skill's folder. ffmpeg needs the vidstab filters. Every clip must start at time zero, because a non-zero start shows as black frames at the head in Resolve. Add setpts=PTS-STARTPTS when using -ss with a time filter, and check start_time with ffprobe.

A hand drifting into the last frames of a still shot can be hidden by holding an earlier frame: trim=end_frame=N,setpts=PTS-STARTPTS,tpad=stop_mode=clone:stop_duration=0.6.

Colour

Colour here means correction: neutral whites, natural skin, true product colours, matching shots. A creative look comes last and stays small, and the product colours still have to measure true after it.

Pick the route by what the shot needs:

  • Whole-frame moves only (a cast, exposure, contrast, saturation): scripts/grade.py. It is faster and repeatable, and the numbers stay in a command you can rerun.
  • One part of the frame needs its own fix (skin under different light, one object): DaVinci Resolve, for its Power Windows.

Both routes use the same measure, adjust, measure loop and the same targets below.

With grade.py

python -I scripts/grade.py still c1.mp4 1.5 before.png
python -I scripts/measure_grade.py before.png --patch counter:40,60,200,220 --skin 100,1450,1000,1920
python -I scripts/grade.py make c1.cube --frame before.png --neutral 40,60,200,220 --white 205 --contrast 1.06 --pivot 0.4 --lift -0.02
python -I scripts/grade.py still c1.mp4 1.5 check.png --lut c1.cube --compare
python -I scripts/grade.py apply c1.mp4 c1g.mp4 --lut c1.cube

--neutral is a patch that should be grey or white: a counter, a wall, paper, a white lid. make equalises its R, G and B, and --white sets the level it ends at. Choose a patch that is evenly lit and not blown out. Look at the --compare frame at full size, and measure the graded half, before applying. One LUT per shot, because each shot has its own cast.

Add --knee 0.85 when a gain pushes highlights past 250. Use --denoise light only on visibly noisy low-light clips, and --sharpen rarely: phones sharpen already, and more gives halos on edges and text.

A grade made in Resolve can leave it as a LUT: right-click the clip's thumbnail on the Color page, Generate LUT, 33 Point Cube. grade.py apply --lut then puts it on any clip. A LUT holds whole-frame moves only, so windows are not in it.

In DaVinci Resolve free

Copy the clips into a plain folder Resolve can see (it cannot open some app-data folders and says "Path does not exist"). Put them on a new timeline and open the Parade scope.

Work in this order, because each step depends on the one before:

  1. Balance node. Gain for the white level, Temp for the cast. Positive Temp warms. The same surface in two shots must match.
  2. Contrast node. Contrast and Pivot, Lift for deep blacks, a small Sat move of 5 or less.
  3. Subject node, only when a global move would hurt something else. Use a soft Power Window. On the test reel a window fixed a hand's skin and left the product colours untouched.
  4. Render, measure, adjust, and repeat. Two passes were enough.
python -I scripts/measure_grade.py frame.png --patch counter:40,60,200,220 --skin 100,1450,1000,1920

Targets

  • Neutral surfaces: R, G and B within about 3 to 5. White around 195 to 215 for a bright look.
  • Skin hue near 123 degrees. Keep it natural, because the vectorscope line is a reference, not a target.
  • Skin brightness: guides put it between 40% and 70% depending on the skin and the scene. Use that range to catch a mistake, not as a number to hit.
  • Highlights under about 2% of pixels at 250+.
  • Saturation: compare the chroma line before and after. A correction should leave p95 close to where it was. A large jump means the grade is adding colour the scene did not have.
  • Product colours stay true when the video sells a product.
  • Two shots of the same surface measure the same.

What moves what:

  • Tint barely moved a red palm. Per-channel Gamma fixed it: G up, R and B down a little.
  • Temp +300 overshot a blue cast where +130 was right. Start small.

Arabic text in Remotion

assets/reelTemplate.tsx is a working template. It keeps each text block as data and has a probe composition for the text check. references/remotion-setup.md covers starting a project from nothing and registering the compositions.

Rules:

  • A heavy Arabic face (the template uses Alexandria 800), white plus one accent colour, no boxes, a soft shadow.
  • Sizes 62 to 124 px on a 1080x1920 frame. A phone needs them that big.
  • Line box 1.5 for Arabic. At 1.22 the letter tails hit the next line.
  • Every letter inside x 110-950 and y 220-1475, so Instagram's right rail and bottom bar cover nothing.
  • whiteSpace: nowrap, so a line never wraps by surprise.
  • Hook on at frame 0. Later lines about 1 s apart, with a 6-frame rise and fade.
  • A soft dark band behind the text keeps the subject visible below it.
  • Customer quotes stay word for word, with no name or photo unless the customer agreed.
  • Say what the viewer is seeing. Do not let a label imply it is the only option.

After any text change, render the probe frames and check them:

npx remotion still ReelProbe out/p0.png --frame=0
python -I scripts/check_text.py out/p0.png out/p1.png out/p2.png out/p3.png

It fails when the ink of two lines comes within 14 px or a letter leaves the safe box. A block holds seven lines at most.

Then check the text against the real picture. Render the last frame of each shot, when every line is on screen, and pass those frames in the same order:

npx remotion still Reel out/r0.png --frame=119
python -I scripts/check_text.py out/p0.png out/p1.png --real out/r0.png out/r1.png

Each line gets a contrast figure against the brightest part of the picture around its letters, and under 3.0 fails. On the test reel the accent-colour lines scored 3.2 to 4.6 and the white lines 5 and up. A low score means a stronger band behind the text, or moving the block.

The default box is Instagram's. For a reel that also goes to TikTok or Shorts, add --platform all. Those two boxes come from third-party guides, so preview the upload in each app.

Then look at full-size frames at the end of every shot. A thumbnail sheet hides collisions, and things moving behind the text only show at full size. On the test reel a spoon crossed a line. A small zoom with a corner origin moved it clear and also cropped a stray finger.

Render and sound

npx remotion render Reel out/reel.mp4 --codec=h264 --crf=15 --muted --color-space=bt709
python -I scripts/beat_offset.py music.ogg --cuts 3.3,6.5,10.3 --length 13.5 --from 0 --to 40

--color-space=bt709 matters. Without it Remotion writes full-range bt470bg with unknown primaries.

Cut the music from the best offset, fade the last 1.2 s, join with -c:v copy, then master:

sh scripts/master.sh in.mov out.mp4 -14 0.4 0.75

The last number is the limiter ceiling. AAC adds overshoot above it, and the amount depends on the track, so the script measures the file it wrote and prints its loudness and true peak. If it prints TOO HOT (true peak above -1 dBTP), run it again with 0.7.

Phone speakers lose most sound below 300 Hz, so check that band:

ffmpeg -hide_banner -nostats -i out.mp4 -vn -af "highpass=f=300:poles=2,highpass=f=300:poles=2,lowpass=f=12000,ebur128" -f null - 2>&1 | tail -12

Aim for -20 LUFS or louder in that band on a -14 LUFS master. A bass-heavy track measured -23.9 and sounded dull on phones. Draw the waveform and spectrum too, because a reviewer model's timestamps do not always match them.

AI-generated music or voice needs the platform's AI label. Generate music slightly longer than the reel, because a track cut short cannot be extended cleanly.

Checks before calling it done

  • ffprobe shows yuv420p, tv, bt709, bt709, bt709, and the right duration and frame count.
  • check_text.py prints PASS, with --real frames included.
  • master.sh printed a true peak at or under -1 dBTP.
  • A cover frame exists if the reel shows on a profile grid (references/other-cases.md).
  • Look at full-size frames yourself, including the first and last frames and the frame before each cut.
  • If a reviewer model watches the final mp4, give it the decisions already made so it does not reopen them.
  • State what was not checked. An agent cannot hear the audio or see the file on a phone.
  • After replacing a file that is already uploaded somewhere, re-upload it, verify it from the public link, and update anything scheduled that points to the old file.

Publishing needs the client's approval.

Related Skills