Edl Tighten logo

Edl Tighten

Community
mrpaidsocial
edl-tighten

Surgically remove awkward pauses, dead air, and long silences from a FINISHED voiceover-driven cut while keeping video, VO audio, word-synced captions, SFX hits, and the composition timeline in sync. Use when asked to "tighten the cut", "remove the pause at 0:41", "cut the dead air", "the gap between sentences is too long", "shave the silences", "it drags here", or when a revision round adds new cuts to an already-tightened edit. Finds gaps two ways (silencedetect + RMS envelope), chooses frame-quantized cut windows that never clip speech onsets, masks splices at cuts or attention magnets, executes ONE trim/concat filtergraph per stream from the original source, remaps the transcript through the EDL with scripts/remap-transcript.py (never re-whisper), and verifies with a re-scan plus frame extraction. Needs ffmpeg/ffprobe + python3 only. Not for building the edit itself (branded-ad-edit / talking-head-recut) or QA of a render you didn't cut (video-qa).

Overview

Publishermrpaidsocial
Repositorymrpaidsocial-video-editor-agent
Skill nameedl-tighten
Stars
114
Forks
34
Bundled files
1
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by mrpaidsocial on GitHub. Read the source before you install it.

Installation

Install the Edl Tighten AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/mrpaidsocial/mrpaidsocial-video-editor-agent/tree/main/.claude/skills/edl-tighten
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/mrpaidsocial/mrpaidsocial-video-editor-agent.git /tmp/mrpaidsocial-video-editor-agent
mkdir -p .claude/skills
cp -r /tmp/mrpaidsocial-video-editor-agent/.claude/skills/edl-tighten .claude/skills/edl-tighten
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Edl Tighten in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Edl Tighten on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Edl Tighten is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

edl-tighten — surgical pause removal that keeps everything in sync

A finished VO-driven edit is a stack of synced layers: video, loudness-normalized voiceover, word-synced captions, SFX hits, and a composition timeline whose card positions were placed against the VO. Cutting 0.4s of dead air out of the audio alone desyncs every other layer. This skill removes the pause from all layers at once by treating the edit as one EDL (a list of removed source ranges) and regenerating every stream from the original source through that EDL.

The iron law: ONE cumulative EDL against the ORIGINAL source. Never cut an already-cut file. Every revision round appends ranges to the same EDL (in source time), merges adjacent/overlapping ranges, and regenerates from source. Cuts-of- cuts accumulate re-encode generations, drift the frame grid, and make timestamps unmappable.

Requirements

  • ffmpeg / ffprobe on PATH
  • python3 (stdlib only — scripts/remap-transcript.py has no dependencies)
  • The original source video, the loudness-normalized VO track that the composition actually uses, and the word-level transcript.json produced when the edit was built (whisper word timestamps). If the VO is baked into the video and no separate track exists, the same EDL still applies — cut the muxed file's audio with the identical atrim graph.
  • Know the composition fps (usually 30). Everything quantizes to that grid.

Pipeline

1 FIND      silencedetect + RMS envelope (BOTH — each misses gaps the other catches)
2 CHOOSE    frame-quantized cut windows, 0.10-0.15s pause kept, onsets protected
3 PLACE     put the splice where it's visually masked
4 EXECUTE   one trim/concat filtergraph per stream, from the original source
5 REMAP     transcript, timeline positions, SFX hits, caption ranges, duration
6 VERIFY    ffprobe + re-scan + frame-extract around each splice and LOOK

1. FIND the gaps — two detectors, both required

1a. silencedetect (hard silences)

bash
ffmpeg -hide_banner -i vo_normalized.wav \
  -af silencedetect=noise=-38dB:d=0.22 -f null - 2>&1 | grep silence_

noise=-38dB:d=0.22 is the tuned setting for normalized VO: it flags every stretch ≥ 0.22s that sits below −38 dB. Run it on the full track first to get the candidate list, then on the region the reviewer flagged.

1b. RMS envelope print (breathy lulls silencedetect misses)

silencedetect alone is not enough. Breath tails, mouth noise, and room tone often sit at −28 to −36 dB — above the silence threshold, but perceptually still dead air. A "pause that drags" flagged by a human reviewer is very often one of these. Print a 50 ms RMS envelope of the suspect region and read it as a bar chart:

bash
# Envelope of source seconds 40-46 (adjust -ss / -t to the suspect region)
ffmpeg -v error -i vo_normalized.wav -ss 40 -t 6 -ac 1 -ar 16000 -f s16le - \
| python3 -c '
import sys, struct, math
data = sys.stdin.buffer.read()
n = len(data)//2
smp = struct.unpack(f"<{n}h", data[:n*2])
sr, t0 = 16000, 40.0            # t0 = the -ss value above
win = int(sr*0.050)             # 50 ms windows
for i in range(0, n - win, win):
    w = smp[i:i+win]
    rms = math.sqrt(sum(s*s for s in w)/win)/32768.0
    db = 20*math.log10(rms) if rms > 0 else -90.0
    bar = "#" * max(0, int((db+60)/1.5))
    print(f"{t0+i/sr:7.2f}s {db:6.1f} dB |{bar}")
'

Reading it:

  • < −38 dB sustained → hard silence (silencedetect saw it too).
  • −28 to −36 dB sustained → breathy lull. Audibly a gap. Cut candidate.
  • A sharp jump of 15+ dB in one window → a speech onset. Mark its time; you must never cut into it (step 2).

1c. Do NOT trust whisper word times for gap boundaries

Whisper word-level timestamps overshoot into silence — a word's end is routinely 0.1–0.4s later than the word actually ends, because whisper pads into the pause. Word ends tell you roughly where a gap is, never how wide it is. The envelope is the ground truth for both edges of the gap. (The same overshoot is why remap-transcript.py clamps word ends at splices in step 5.)

2. CHOOSE the cut window

For each gap, pick the removed range [cut_start, cut_end] in source time:

  1. Leave 0.10–0.15s of natural pause. Butt-splicing two phrases with zero gap sounds robotic and creates an audible bump. If the gap is 0.62s, remove ~0.48s and keep ~0.14s.
  2. Never clip a speech onset. Set cut_end from the envelope, not from the next word's whisper start (also unreliable). Find the onset jump in the envelope and keep at least one frame of margin before it (cut_end ≤ onset − 1/fps). Clipping the first plosive of the next word is the most audible mistake this workflow can make.
  3. Frame-quantize both edges to the fps grid. At 30 fps every boundary must be a multiple of 1/30 s (0.0333…). Round cut_start up and cut_end down (shrink the cut, never grow it) so quantization can't eat the safety margin. Un-quantized cuts make the video and audio graphs disagree by sub-frame amounts and produce one-frame freezes at splices.

Record each range in the EDL file (see Bookkeeping below) before executing.

3. PLACE the splice where it's masked

A splice in a continuous talking-head shot is a jump cut — the speaker's head/hands teleport. Sometimes acceptable, but always check whether the cut can land somewhere the eye won't see it:

  • At a framing or section cut that already exists in the edit (a punch-in, a B-roll cut, a scene change). If a section boundary sits inside or near the gap, put the splice exactly on it — a cut on a cut is invisible.
  • Exactly where an attention magnet lands — a card popping in, a big headline hit, a screenshot sliding on. The motion graphic steals the eye for ~10 frames; a jump on the speaker under it goes unnoticed. If the timeline has a card entering near the gap, bias the splice to land on the card's entrance frame.
  • If it must land mid-shot with nothing to mask it: expect a visible jump on the speaker and say so when reporting the change. Options: accept it (fast social pacing tolerates jump cuts), add a 2–4% punch-in on one side of the splice, or drop a brief overlay on the splice frame.

4. EXECUTE — one filtergraph per stream, from the original source

Build the keep-segments from the merged EDL (keeps = the complement of the cuts) and cut each stream in a single pass. Never run ffmpeg on a previous round's output.

With cuts 41.400–41.833 and 62.100–62.500 in a 75.000s source: keeps = 0–41.400, 41.833–62.100, 62.500–75.000.

Video (from the original source video):

bash
ffmpeg -y -i source.mp4 -filter_complex "
[0:v]split=3[v0][v1][v2];
[v0]trim=0:41.400,setpts=PTS-STARTPTS[va];
[v1]trim=41.833:62.100,setpts=PTS-STARTPTS[vb];
[v2]trim=62.500:75.000,setpts=PTS-STARTPTS[vc];
[va][vb][vc]concat=n=3:v=1:a=0[vout]" \
  -map "[vout]" -an \
  -c:v libx264 -crf 18 -preset medium -pix_fmt yuv420p \
  -g 30 -keyint_min 30 \
  video_tight.mp4
  • split=N → one trim=A:B,setpts=PTS-STARTPTS per keep → concat. One graph, one re-encode, N segments.
  • -g 30 -keyint_min 30 is mandatory (match the number to the fps): a keyframe every second keeps seeking frame-accurate when the tightened video is placed in a composition/player that scrubs. Default GOP sizes (250) make post-splice seeks land seconds off.
  • Re-encoding is unavoidable and correct here — stream-copy concat can only cut on existing keyframes and will not hit your frame-quantized boundaries.

Audio — the identical boundaries as an atrim graph on the loudness-normalized VO (not the raw VO; cutting the raw VO and re-normalizing changes the level of the whole track):

bash
ffmpeg -y -i vo_normalized.wav -filter_complex "
[0:a]asplit=3[a0][a1][a2];
[a0]atrim=0:41.400,asetpts=PTS-STARTPTS[aa];
[a1]atrim=41.833:62.100,asetpts=PTS-STARTPTS[ab];
[a2]atrim=62.500:75.000,asetpts=PTS-STARTPTS[ac];
[aa][ab][ac]concat=n=3:v=0:a=1[aout]" \
  -map "[aout]" vo_tight.wav

Same numbers, same order. If video and audio boundaries differ by even one frame, lipsync drifts after the splice for the rest of the piece.

Music beds and ambient tracks usually should not be EDL-cut (a music splice is audible); keep them continuous and just shorten the tail to the new duration, unless a music hit was synced to a VO moment — then re-place it in step 5 like an SFX hit.

5. REMAP everything downstream

The EDL changes every timestamp after the first cut. Remap in code, never by re-deriving:

Transcript / captions — run the bundled script:

bash
python3 .claude/skills/edl-tighten/scripts/remap-transcript.py \
  transcript.json --cuts edl.json --fps 30 -o transcript.tight.json

It shifts every word's start/end through the EDL, clamps word ends that overshoot into a removed range to the splice point, drops entries that were entirely inside a cut (warns — verify those were silence), and preserves the JSON structure (OpenAI whisper segments[].words[], flat word lists, and whisper.cpp ms offsets all supported).

NEVER re-whisper the tightened audio. Whisper on concatenated cuts drifts late — errors compound across splices and by the end of the piece karaoke captions highlight the wrong word. The remap is exact; a fresh transcription is not. This is the single most tempting shortcut in this workflow and it always loses.

Everything else with a timestamp — map each one with the same tool:

bash
python3 .claude/skills/edl-tighten/scripts/remap-transcript.py \
  --cuts edl.json --query 44.10,62.30,71.05

Apply the mapped values to:

  • Composition timeline positions — every card/overlay/B-roll clip start and duration that lands after the first cut (a clip spanning a cut also shortens: map its start and end separately).
  • SFX hit times — whooshes/pops synced to card entrances move with their cards.
  • Caption config ranges — any per-range styling or section boundaries expressed in seconds.
  • Stage/composition duration — new duration = old duration − total removed (the script prints both). Update it explicitly; a stale duration leaves frozen frames or trailing black at the end of the render.

6. VERIFY

  1. Durations: ffprobe -v error -show_entries format=duration -of csv=p=0 video_tight.mp4 (and the wav). Both must equal source − total_removed within one frame, and match each other.

  2. Re-scan the target region: re-run silencedetect and the envelope print on vo_tight.wav around each splice. Expect zero gaps ≥ 0.15s in the tightened region, and no clipped onset (the first window of the next phrase should jump straight to speech level, not ramp from a chopped plosive).

  3. Frame-extract around each splice and LOOK — this step is not optional; a graph cannot see a jump cut:

    bash
    # splice at output time T, at 30 fps — 3 frames either side
    ffmpeg -y -ss $(echo "$T - 3/30" | bc -l) -i video_tight.mp4 \
      -frames:v 7 -vf fps=30 splice_%02d.png

    Read the frames. Check: no freeze/duplicate frame, no black frame, and whether the speaker jump (if mid-shot) is acceptable. If a card was used as the mask, confirm it actually enters on the splice frame.

  4. If the tightened video feeds a composition, re-render and spot-check the captions around each splice — the karaoke highlight must stay on the spoken word after every cut.


Bookkeeping — one EDL, every revision round

Keep a single edl.json next to the project, always in source time:

json
{
  "source": "source.mp4",
  "fps": 30,
  "cuts": [
    [41.400, 41.833],
    [62.100, 62.500]
  ]
}

Rules that make round 3 as clean as round 1:

  • Append, merge, regenerate. A new reviewer note ("also tighten the pause at 55s") = find the gap in the source (source time ≈ output time + total removed before it; confirm with the envelope on the source VO), append the range, then merge and regenerate everything from the original source:

    bash
    python3 .claude/skills/edl-tighten/scripts/remap-transcript.py \
      --cuts edl.json --write-merged edl.json --query 0

    (merge combines overlapping/adjacent ranges automatically — two revision rounds nibbling at the same pause become one clean cut.)

  • Never express a new cut in tightened-file time and cut the tightened file. Convert to source time, add to the EDL, re-run step 4. The extra re-encode you "save" costs you the ability to remap anything ever again.

  • The original transcript.json (against the source) is likewise immutable — every round re-remaps it through the full merged EDL in one shot.

  • Timeline/SFX/caption positions are best kept in source time in your working notes too, mapped through the EDL at build time; if the composition stores output-time values, recompute all of them from the source-time originals each round rather than incrementally shifting them.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Edl Tighten AI skill do?

Surgically remove awkward pauses, dead air, and long silences from a FINISHED voiceover-driven cut while keeping video, VO audio, word-synced captions, SFX hits, and the composition timeline in sync. Use when asked to "tighten the cut", "remove the pause at 0:41", "cut the dead air", "the gap between sentences is too long", "shave the silences", "it drags here", or when a revision round adds new cuts to an already-tightened edit. Finds gaps two ways (silencedetect + RMS envelope), chooses frame-quantized cut windows that never clip speech onsets, masks splices at cuts or attention magnets,...

Why use Edl Tighten on TypingMind?

Because you install it once and use it with any model. Edl Tighten is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Edl Tighten in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/mrpaidsocial/mrpaidsocial-video-editor-agent/tree/main/.claude/skills/edl-tighten. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Edl Tighten?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Edl Tighten?

As many as you like. As long as a model supports skills, you can use Edl Tighten with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Edl Tighten AI skill free?

It is published on GitHub by mrpaidsocial. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇