[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f3b0s16yrrf58x":3},{"id":4,"title":5,"description":6,"category":7,"contentHtml":8,"date":9,"createdAt":10,"updatedAt":11},"ebd62337-5325-44f6-84e6-081937c8e218","Adding Impact Effects to Fight Videos Using AI","I built an API that generates impact effects for any video (preferably a fighting one ig). You know, those dramatic black and white flashes right when someone gets hit. Pretty cool right? Here's how I did it in Go.","backend development","\u003Cp>The goal: take a fight video (anime, boxing, UFC, whatever) and automatically add those dramatic black and white flash effects at the exact moments where someone gets hit. You know, the ones you see in anime edits and fight compilations on TikTok.\u003C\u002Fp>\n\u003Cp>The idea is simple: upload a video, let AI figure out where the hits land, apply the effects, get the video back.\u003C\u002Fp>\n\u003Ch2>The Problem\u003C\u002Fh2>\n\u003Cp>Doing this manually in a video editor is tedious. You have to scrub through the video frame by frame, find every impact moment, add your effect, adjust the timing, repeat. For a 30 second clip with 5 impacts that might take 15 minutes. For longer videos it gets worse.\u003C\u002Fp>\n\u003Cp>I wanted a single API call that handles the whole thing.\u003C\u002Fp>\n\u003Ch2>The Stack\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Go\u003C\u002Fstrong> with Gin for the HTTP server\u003C\u002Fli>\n\u003Cli>\u003Cstrong>FFmpeg\u003C\u002Fstrong> for all the video processing (frame extraction, labeling, grid creation, effect application)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Google Gemini\u003C\u002Fstrong> (via the genai SDK) for analyzing frames and finding impact moments\u003C\u002Fli>\n\u003Cli>\u003Cstrong>PostgreSQL\u003C\u002Fstrong> with sqlc for the database layer\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The project follows a hexagonal architecture. Ports, adapters, domain, services. Keeps things clean and testable.\u003C\u002Fp>\n\u003Ch2>Step 1: Extract and Label Frames\u003C\u002Fh2>\n\u003Cp>The first thing the pipeline does after receiving a video upload is extract frames at 10 frames per second. Each frame gets a visible number burned into the top left corner. This number is how the AI will reference specific frames later.\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> ExtractAndLabelFrames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">inputPath\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">outputDir\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">sampleRate\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> float64\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) (\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">*\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">ExtractedFrames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">error\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    if\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> os.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">MkdirAll\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(outputDir, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">0755\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">); err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">!=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\"> nil\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">        return\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\"> nil\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Errorf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"failed to create output dir: \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%w\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, err)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    filterStr \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"fps=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%g\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">,scale=640:-2,drawtext=text='\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%%\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">{eif\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\">\\\\\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">:n+1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\">\\\\\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">:d}':x=10:y=10:fontsize=28:fontcolor=white:borderw=2:bordercolor=black\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        sampleRate,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    outputPattern \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> filepath.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Join\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(outputDir, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"frame_\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%05d\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">.jpg\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    args \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-y\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-i\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, inputPath,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-vf\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, filterStr,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-q:v\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"3\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-f\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"image2\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        outputPattern,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    cmd \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> exec.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Command\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"ffmpeg\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, args\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">...\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    output, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> cmd.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">CombinedOutput\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    if\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">!=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\"> nil\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">        return\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\"> nil\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Errorf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"ffmpeg extract+label error: \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%w\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\">\\n\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">output: \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, err, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(output))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F ... glob and sort the frame paths, get metadata\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>The \u003Ccode>drawtext\u003C\u002Fcode> filter here uses \u003Ccode>%{eif\\:n+1\\:d}\u003C\u002Fcode> to burn the frame number as an integer. I initially used \u003Ccode>%{expr\\:n+1}\u003C\u002Fcode> but that gave me numbers like \u003Ccode>1.000000\u003C\u002Fcode> instead of just \u003Ccode>1\u003C\u002Fcode>. The \u003Ccode>eif\u003C\u002Fcode> variant formats it as a decimal integer.\u003C\u002Fp>\n\u003Cp>For a 30 second video at 10fps, this gives you 300 labeled frames.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fres.cloudinary.com\u002Fdb6nohcui\u002Fimage\u002Fupload\u002Fv1777318431\u002Fportfiolio-blog\u002Fo6kdfypucaxuxet6v7fn.jpg\" alt=\"Labeled frame example\">\u003C\u002Fp>\n\u003Ch2>Step 2: Arrange Frames Into Grids\u003C\u002Fh2>\n\u003Cp>Sending 300 individual images to Gemini would be expensive and slow. Instead, I tile the frames into 5x4 grids. That is 20 frames per grid image. A 30 second video now becomes about 15 grid images instead of 300 individual frames.\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> (\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">    gridCols\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">      =\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">    gridRows\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">      =\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 4\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">    framesPerGrid\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> gridCols \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">*\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> gridRows\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>Each grid is built using FFmpeg's concat demuxer and tile filter:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> createSingleGrid\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">framePaths\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">outputPath\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">error\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F Write a concat list so ffmpeg reads the frames as a sequence\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    listPath \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> outputPath \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">+\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> \".txt\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    var\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> listContent \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">strings\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Builder\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    for\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> _, p \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> range\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> framePaths {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        abs, _ \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> filepath.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Abs\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(p)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        listContent.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">WriteString\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"file '\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">'\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\">\\n\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, filepath.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">ToSlash\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(abs)))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        listContent.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">WriteString\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"duration 0.04\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\">\\n\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    os.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">WriteFile\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(listPath, []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">byte\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(listContent.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">String\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">()), \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">0644\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    tileLayout \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%d\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">x\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%d\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, gridCols, gridRows)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    args \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-y\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-f\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"concat\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-safe\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"0\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-i\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, listPath,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-vf\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"scale=640:-2,tile=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, tileLayout),\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-frames:v\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"1\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"-q:v\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"3\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        outputPath,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    cmd \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> exec.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Command\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"ffmpeg\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, args\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">...\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F ...\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>If the last batch has fewer than 20 frames, the tile filter fills the empty cells with black. No special handling needed.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fres.cloudinary.com\u002Fdb6nohcui\u002Fimage\u002Fupload\u002Fv1777318375\u002Fportfiolio-blog\u002Fhdqvgsz2o3uorzaddclk.jpg\" alt=\"5x4 grid of labeled frames\">\u003C\u002Fp>\n\u003Ch2>Step 3: Let Gemini Find the Impacts\u003C\u002Fh2>\n\u003Cp>The grid images get uploaded to Gemini via the File API. Then I send a prompt that tells the model exactly what to look for and what to ignore.\u003C\u002Fp>\n\u003Cp>The system prompt is pretty specific:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"markdown\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"markdown\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">A key action moment is when two things make significant physical contact:\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> A character's motion connecting with another character or object\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> A fast-moving object reaching its target\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Two characters or objects colliding with visible force\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> A character making contact with a surface with force\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>And equally specific about what to skip:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"markdown\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"markdown\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Movement without any contact (running, jumping, flying)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Preparation or wind-up before contact happens\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Characters standing, posing, talking, or reacting\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> The aftermath of contact (character already moving away)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Black screens or dark frames between scenes\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> Scene transitions, fades, cuts between angles\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>The model returns a simple JSON response:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"json\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"json\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{ \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">\"impacts\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">: [\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">12\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">45\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">78\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">132\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">] }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>Just frame numbers. Nothing else. The prompt explicitly says to pick only the 2 to 5 best moments. Quality over quantity.\u003C\u002Fp>\n\u003Cp>The grids are uploaded concurrently with a buffered channel to avoid overloading the API:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">const\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\"> maxConcurrent\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> =\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 5\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">sem \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> make\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">chan\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> struct\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{}, maxConcurrent)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">ch \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> make\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">chan\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> result\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">len\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(files))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">for\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> i, f \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> range\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> files {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    sem \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> struct\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{}{}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    go\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">i\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> int\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">f\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> llm\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">File\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">        defer\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">() { \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">sem }()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        uf, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> g.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">uploadFile\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(ctx, f)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        ch \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> result\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{index: i, file: uf, err: err}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }(i, f)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Ch2>Step 4: Apply the Effects\u003C\u002Fh2>\n\u003Cp>This is the interesting part. The original approach was to re-extract frames from the video at 10fps, apply the effect to the impact frames individually, then stitch everything back together. That worked but the output was choppy because you are reconstructing a video at 10fps regardless of what the original framerate was.\u003C\u002Fp>\n\u003Cp>The fix is to convert the impact frame numbers to timestamps and apply the effect filters directly to the original video. FFmpeg has an \u003Ccode>enable\u003C\u002Fcode> parameter on its filters that lets you turn them on and off based on time.\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> BuildImpactVideo\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">inputPath\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">outputDir\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">impactFrames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">int\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">sampleRate\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> float64\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">meta\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> *\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">VideoMetadata\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) (\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">error\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F Convert frame numbers to time windows\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    type\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> timeWindow\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> struct\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        start \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">float64\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        end   \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">float64\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    var\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> windows []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">timeWindow\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    for\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> _, f \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> range\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> impactFrames {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        startFrame \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> f\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">        if\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> startFrame \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">            startFrame \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        endFrame \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> f \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">+\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 1\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        startTime \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> float64\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(startFrame\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">\u002F\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> sampleRate\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        endTime \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> float64\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(endFrame) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">\u002F\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> sampleRate\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        windows \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> append\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(windows, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">timeWindow\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{startTime, endTime})\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F Sort and merge overlapping windows\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    sort.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Slice\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(windows, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">i\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">j\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> int\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">bool\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> { \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">return\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> windows[i].start \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> windows[j].start })\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    var\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> merged []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">timeWindow\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    for\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> _, w \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> range\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> windows {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">        if\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> len\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(merged) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">>\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\"> 0\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> &#x26;&#x26;\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> w.start \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x3C;=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> merged[\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">len\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(merged)\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">].end {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">            if\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> w.end \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">>\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> merged[\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">len\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(merged)\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">].end {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">                merged[\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">len\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(merged)\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">-\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">1\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">].end \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> w.end\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">            }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        } \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">else\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">            merged \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> append\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(merged, w)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F Build enable expression\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    var\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> parts []\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">string\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    for\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> _, w \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> range\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> merged {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        parts \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\"> append\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(parts, fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"between(t,\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%.3f\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">,\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%.3f\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">)\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, w.start, w.end))\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    enableExpr \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> strings.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Join\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(parts, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"+\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    filter \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> fmt.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Sprintf\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"hue=s=0:enable='\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">',eq=contrast=3.5:brightness=0.9:enable='\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">',curves=all='0\u002F0 0.25\u002F0.1 0.5\u002F0.6 0.75\u002F0.95 1\u002F1':enable='\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">%s\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">'\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">        enableExpr, enableExpr, enableExpr,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F ... run ffmpeg with the filter\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>The \u003Ccode>enable='between(t,4.000,4.200)'\u003C\u002Fcode> part tells FFmpeg to only apply that filter between 4.0 and 4.2 seconds. The rest of the video passes through untouched. This means the output keeps the original video's native framerate. If your input is 60fps, the output is 60fps. No choppiness.\u003C\u002Fp>\n\u003Cp>The effect itself is a chain of three filters:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Ccode>hue=s=0\u003C\u002Fcode> removes all color (full desaturation)\u003C\u002Fli>\n\u003Cli>\u003Ccode>eq=contrast=3.5:brightness=0.9\u003C\u002Fcode> cranks the contrast way up\u003C\u002Fli>\n\u003Cli>\u003Ccode>curves\u003C\u002Fcode> applies a custom tone curve for that punchy look\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>The Full Pipeline\u003C\u002Fh2>\n\u003Cp>Putting it all together, the handler orchestrates everything in one request:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"go\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"go\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">func\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> (\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">h \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">*\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Handler\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">ExtractFrames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#E36209\">c\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\"> *\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">gin\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Context\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 1. Receive video upload (max 1GB)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    file, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> c.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">FormFile\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"video\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 2. Extract labeled frames at 10fps\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    extracted, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> videoService.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">ExtractAndLabelFrames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(inputPath, framesDir, sampleRate)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 3. Create 5x4 grids\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    gridPaths, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> videoService.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">CreateGridImages\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(extracted.Paths, gridsDir)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 4. Upload grids to Gemini\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    uploadedFiles, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> h.llm.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">UploadFiles\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(llmFiles)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 5. Send prompt + grids to Gemini\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    llmResponse, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> h.llm.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">GenerateText\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(fullPrompt, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">false\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, uploadedFiles)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 6. Parse the frame numbers from the response\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">    var\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> impactResponse \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">domainVideo\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">ImpactResponse\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    json.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Unmarshal\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">([]\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">byte\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(jsonStr), \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">&#x26;\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">impactResponse)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 7. Build the output video with effects applied\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    outputPath, err \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#D73A49\">:=\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\"> videoService.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">BuildImpactVideo\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(inputPath, tempDir, impactResponse.Impacts, sampleRate, meta)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\"> \u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\">    \u002F\u002F 8. Return download URL\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    response.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">NewSuccessResponse\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">\"Impact video generated successfully\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">gin\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">.\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">H\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"download_url\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">: downloadURL,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"impacts\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">:      impactResponse.Impacts,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\">        \"llm_cost\"\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">:     llmResponse.Dollars,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">    }, \u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#005CC5\">nil\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">).\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">Send\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">(c)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFF;--shiki-light:#24292E\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>Upload a video, get back a download URL. The response also includes which frame numbers were detected and how much the Gemini API call cost.\u003C\u002Fp>\n\u003Ch2>Example Output\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Before (original):\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>\u003Cvideo src=\"https:\u002F\u002Fres.cloudinary.com\u002Fdb6nohcui\u002Fvideo\u002Fupload\u002Fv1777318528\u002Fbefore-impact-demo_fpehmn.mp4\" controls muted playsinline width=\"100%\">\u003C\u002Fvideo>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>After (with impact effects):\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>\u003Cvideo src=\"https:\u002F\u002Fres.cloudinary.com\u002Fdb6nohcui\u002Fvideo\u002Fupload\u002Fv1777318152\u002Fafter-impact-demo_xkxv5d.mp4\" controls muted playsinline width=\"100%\">\u003C\u002Fvideo>\u003C\u002Fp>\n\u003Ch2>Things To Note\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>FFmpeg escaping on Windows.\u003C\u002Fstrong> The filter strings have nested single quotes (the \u003Ccode>enable='...'\u003C\u002Fcode> and \u003Ccode>curves=all='...'\u003C\u002Fcode> both use them). On Windows, the command line argument parsing was mangling them. I ended up writing the filter to a file and using \u003Ccode>-filter_script:v\u003C\u002Fcode> to load it, which bypasses all the escaping issues.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Grid rendering.\u003C\u002Fstrong> I initially tried using FFmpeg's \u003Ccode>xstack\u003C\u002Fcode> filter to arrange frames into grids. It was unreliable. Switching to the \u003Ccode>concat\u003C\u002Fcode> demuxer plus the \u003Ccode>tile\u003C\u002Fcode> filter was way more straightforward.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Gemini content filtering.\u003C\u002Fstrong> Google's safety filters can block fight\u002Fcombat content at the infrastructure level, even with all safety settings set to off. This is a policy level thing, not something you can override via the API. Worth knowing if you are working with action content.\u003C\u002Fp>\n\u003Ch2>Try It Out\u003C\u002Fh2>\n\u003Cp>The full source code is on GitHub: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fumohsamuel\u002Fimpact\">github.com\u002Fumohsamuel\u002Fimpact\u003C\u002Fa>\u003C\u002Fp>\n\u003Cp>You need Go, FFmpeg, and a Google Gemini API key to run it locally.\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"bash\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"bash\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">git\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> clone\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> https:\u002F\u002Fgithub.com\u002Fumohsamuel\u002Fimpact.git\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#005CC5\">cd\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> impact\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">go\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> mod\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> download\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#8B8B8B94;--shiki-light:#6A737D\"># set up your .env\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">go\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> build\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#005CC5\"> -o\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> .\u002Ftmp\u002Fmain.exe\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> .\u002Fcmd\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">.\u002Ftmp\u002Fmain.exe\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>\n\u003Cp>Then call the endpoint:\u003C\u002Fp>\n\u003Cfigure data-rehype-pretty-code-figure=\"\">\u003Cpre tabindex=\"0\" data-language=\"bash\" data-theme=\"vesper github-light\">\u003Ccode data-language=\"bash\" data-theme=\"vesper github-light\" style=\"display: grid;\">\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#FFC799;--shiki-light:#6F42C1\">curl\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#005CC5\"> -X\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> POST\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> http:\u002F\u002Flocalhost:5000\u002Fapi\u002Fv1\u002Fgenerate-impact-frames\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#A0A0A0;--shiki-light:#005CC5\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan data-line=\"\">\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#005CC5\">  -F\u003C\u002Fspan>\u003Cspan style=\"--shiki-dark:#99FFE4;--shiki-light:#032F62\"> \"video=@your-video.mp4\"\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Ffigure>","April 27, 2026","2026-04-27T20:04:09.213Z","2026-09-13T14:06:58.324Z"]