Automatic optimization
How vidscroll re-encodes slow-to-scrub videos in the browser, caches them, and what you can configure.
You can give ScrollVideo any MP4 or WebM, including one exported straight
from an editor. If its keyframes are too far apart for smooth scrubbing
(why that matters), vidscroll fixes the
file in the visitor’s browser. This is on by default.
What happens
- Check the cacheIf this file was optimized on an earlier visit, that copy is used and nothing is downloaded.
- DownloadOtherwise the whole file is fetched, so its structure can be read.
- Check keyframesGaps of 0.5 s or less: the file is used as it is.
- Re-encodeOtherwise the browser's video encoder (WebCodecs) makes a copy with close keyframes.
- Swap and storeThe copy replaces the original at the same frame and is saved for next time.
What visitors see
While the re-encode runs, the original video is already on screen and can be scrolled. Scrolling forward plays it smoothly; scrolling backward is choppy until the new copy arrives. A small badge in the corner shows the progress. When the copy is ready, it takes over at exactly the frame on screen, so the switch isn’t visible.
In our tests, a 60-second 1080p clip took about 8 to 10 seconds to re-encode on a desktop computer and about 20 seconds on an Android phone. Shorter clips are proportionally faster.
To keep the loading overlay up until the smooth copy is ready instead, use
wait:
<ScrollVideo src="/videos/hero.mp4" optimize={{ wait: true }} />The re-encoded copy
| Property | Value |
|---|---|
| Keyframes | One every 0.25 s |
| Resolution | Short side at most 720 px (never enlarged) |
| Frame rate | At most 30 fps, or 15 fps for videos over two minutes |
| Codec | H.264 |
| Audio | Removed |
The browser encoder favours speed over size: for the clip above, the copy it made was 18.6 MB, where the CLI produces 11.9 MB at the same resolution. That file lives only in the visitor’s browser, but it’s one more reason to prepare videos ahead of time for production.
Caching
The optimized copy is saved in the browser’s Cache Storage, so later visits load it straight away without downloading or re-encoding again. The cache entry is tied to:
- the video’s URL,
- the optimization settings, so changing
maxResolution,maxFpsormaxKeyframeGapcreates a new copy, - the file’s identity, read from its
ETag,Last-ModifiedorContent-Lengthheader, so replacing the file on the server starts over.
Caching also needs a secure page (HTTPS or localhost), like WebCodecs itself.
Options
Pass an object to optimize to change the defaults, or false to turn
optimization off:
<ScrollVideo src="/videos/hero.mp4" optimize={{ maxResolution: 1080, cache: true }} />| Prop | Type | Default | Description |
|---|---|---|---|
maxKeyframeGap | number | 0.5 | Largest keyframe gap, in seconds, that counts as fine. Files above it are re-encoded. |
maxResolution | number | 720 | Short side of the re-encoded copy, in pixels. Never enlarges. |
maxFps | number | 30 | Frame rate cap for the copy. 15 for videos over two minutes unless set. |
cache | boolean | true | Save the copy for later visits. |
wait | boolean | false | Keep the loading overlay until the copy is ready, instead of showing the original meanwhile. |
When it can’t run
In each of these cases vidscroll still shows the video, and says why in the browser console:
| Situation | What happens |
|---|---|
| The browser has no WebCodecs | The original is used; scrubbing is only as smooth as the file. |
| The re-encode fails | The original stays on screen. |
| The file is on another site without CORS | vidscroll can’t read it, so it streams the file directly instead. |
| Several raw videos on one page in Firefox | Firefox may refuse to encode more than one at a time; the others keep their originals. |
optimize={false} | The file is used as is, with a warning if its keyframes are far apart. |
fullPreload={false} | The file is streamed directly: nothing is downloaded or re-encoded first. |
When to rely on it
Automatic optimization is ideal while building, for content that changes often, and for videos you don’t control. For a production page with a fixed video, pre-encode it with the CLI: the file is smaller, every visitor gets smooth scrubbing on their first visit, and phones skip the work entirely.