Generated video almost never loops on its own — the motion keeps building, so the last frame doesn't match the first and you get a visible jump every cycle. This compares every frame against every other, finds the pair that matches closest, and closes the gap with a cross-dissolve.
A sample clip is loaded below so you can see it working — drop your own over it. Nothing is uploaded. Decoding and encoding happen in this tab.
Every frame compared against every other. Bright means similar, so each bright point away from the diagonal is a pair that could loop. The dim band is too short to be useful. Click a bright point to select it.
The × is the raw seam, before any dissolve — it ranks the windows against each other, and is routinely tens of × even on footage that closes perfectly. The number that says whether a loop worked is under the preview.
Plays the finished loop — dissolve, black point and grade applied — so you can watch the seam before committing to an export.
Longer hides a worse seam, but softens more of the loop.
Lifts a not-quite-black background to true 0.
Pushes neutral toward brass, sparing highlights.
Match how large it actually renders, not the source.
Dark, grainy footage bands below ~4.
Set before analysing; must match the source.
Ready.
Select a loop window to generate the command.
Browser export writes VP9/WebM at a fixed bitrate. For a final MP4 — better control of banding in the blacks — run the same window through looper.py with x264.
Same idea, different medium
An ambience recording almost never loops on its own — the end does not match the beginning, so you hear a click, a jump in level, or the same gust arriving twice. This compares every moment against every other by its spectrum, finds the closest pair, and closes the gap with an equal-power crossfade.
A sample is loaded below — drop your own over it. Nothing is uploaded. Decoding, analysis and export happen in this tab.
Every analysis window compared against every other by its spectrum, not its waveform — two moments can look nothing alike sample by sample and still be the same sound. Bright means similar. Click a bright point away from the diagonal to select that window.
The crossfade region is shaded. It plays as a genuine seamless loop — the browser wraps the buffer itself — so what you hear is what the file will do.
Longer hides a worse seam. Over about 2s it starts eating the loop.
Linear dips ~3 dB mid-fade on noise — audible as a breath.
Only matters at 0 ms crossfade, where the join is a hard cut.
Off by default: a level change is still a change.
Finer finds tighter windows and costs more time.
No browser encodes mp3; encode from the WAV if you need one.
Ready.
Select a loop window to generate the snippet.
The page and the snippet do the same arithmetic. The snippet is there for batching, for lengths that would be tedious to drag around, and because a loop you can regenerate from a script is a loop you still have next year.