Why Browser Video Export Runs Out of Memory
Direct-to-disk export removes one large browser buffer, but decoded frames, PCM audio, canvas surfaces, runtimes, and FFmpeg MEMFS can still exhaust memory.
Saving an edited video directly to disk sounds like a complete solution to browser memory limits. If the output no longer has to sit in a giant Blob, why should the tab run out of memory?
Because the final file is only one part of a media job.
A browser editor may also retain decoded video frames, canvas surfaces, uncompressed audio, encoder state, model tensors, source copies, or a virtual file system. Direct saving can remove a large output buffer, but it cannot make those other allocations disappear.
This distinction shaped a recent ArtPlayer engineering change. We added shared direct-to-disk output to several local media tools, but only after teaching each tool to estimate its complete workload. The result is not an “unlimited file size” switch. It is a safer choice between buffered export, streamed export, and an early rejection when neither plan fits.

The familiar buffered export path
A straightforward browser export often ends like this:
- Decode or transform the source media.
- Send encoded chunks into an in-memory output target.
- Assemble the completed output as an
ArrayBufferorBlob. - Create an object URL.
- Trigger a download.
This is convenient. The page can preview the result, read its size, and offer the same file again without rerunning the job. It is also expensive for a large export.
Near completion, the browser may hold the encoded output inside the media library, another representation used to construct the Blob, and temporary data used by the download path. Those allocations can overlap. The peak matters more than the final file size.
A 500 MB output does not imply a 500 MB peak, and a 500 MB input does not predict the output at all. Duration, resolution, frame rate, codec, bitrate, audio path, and the transformation itself all affect the workload.
What direct-to-disk saving changes
When a compatible browser context exposes a writable file handle, a media pipeline can send encoded chunks to that handle as they become available. ArtPlayer’s Mediabunny path uses a chunked stream target and a bounded output window rather than retaining the complete encoded file in memory.
For fragmented MP4 output, the container can be written incrementally. Once an encoded chunk has safely moved through the bounded stream, the pipeline does not need to keep the whole finished MP4 behind it.
That changes the output term in the memory plan from “estimated file size, possibly with overlapping copies” to a small streaming window. In ArtPlayer’s shared implementation, the default window is 4 MiB.
That is a meaningful reduction, especially for long or high-bitrate exports. But it changes only the encoded-output term.
The memory that direct saving does not remove
The rest depends on what the tool actually does.
Decoded video frames
Compressed video is compact because frames share information across time. A decoder must reconstruct pixels before a canvas effect, geometric transform, or frame reordering operation can use them.
One RGBA surface is approximately:
width × height × 4 bytes
A 3840 × 2160 surface is about 31.6 MiB before counting alignment, decoder-owned frames, staging textures, or additional canvases. A pipeline that needs several live surfaces can consume hundreds of megabytes even while its encoded output streams neatly to disk.
The Reverse Video tool illustrates the difference. It can stream the final MP4, but reversing the visual timeline still requires frame metadata, decoding work, canvas surfaces, and browser-specific frame handling. If audio is reversed too, that creates another large allocation.
PCM audio
Encoded AAC, Opus, or MP3 audio can be small. Decoded PCM is not.
For floating-point samples, a rough estimate is:
duration × sample rate × channels × 4 bytes
One hour of stereo 48 kHz float PCM is about 1.29 GiB. A tool that needs the whole decoded track—for example to reverse samples or analyze a complete waveform—cannot solve that allocation merely by streaming the final MP4.
Some pipelines process audio in bounded blocks. Others have compatibility paths that materialize an entire track. The memory plan has to describe the path that will actually run, not the most optimistic architecture available in theory.
Canvas and GPU resources
Canvas-based video tools commonly maintain a source frame, a destination canvas, encoder input, recovery surfaces, and a small frame pool. GPU textures and browser-internal copies may not appear as ordinary JavaScript heap usage, but they still contribute to the tab’s practical limit.
This is why clearing an array or revoking an object URL is not a complete memory strategy. Ownership extends across JavaScript, the decoder, the graphics stack, WebAssembly, and the browser’s media implementation.
Models and runtime reserves
Browser AI tools add model weights, tensors, intermediate activations, and backend-specific working memory. A streamed output is useful, but the model may dominate the task before encoding starts.
The same principle applies to non-AI runtimes. Encoders, muxers, demuxers, workers, and WebAssembly heaps need working space. A realistic plan reserves capacity for them instead of assuming every byte belongs to the source or result.
FFmpeg.wasm and MEMFS
FFmpeg.wasm has a separate boundary. A typical task writes source files into FFmpeg’s in-memory file system, runs the command, and reads the result back out. The input, output, WebAssembly heap, filter graphs, and temporary files can overlap.
Writing the eventual browser download directly to disk after readFile() does not remove the output that FFmpeg already created in MEMFS. True streaming requires a different execution architecture, not a new download button.
ArtPlayer therefore treats FFmpeg tools such as Compress Video differently from Mediabunny tools. They may share user-facing memory guidance, but they cannot claim the same output-memory reduction until the runtime itself stops materializing the completed file.
Why HLS to MP4 is a useful example
The HLS to MP4 workflow receives a playlist and media segments rather than one local file. Direct saving prevents the assembled MP4 from growing indefinitely in an output buffer, but the job still needs a bounded plan for downloaded data, demuxing, remuxing, runtime overhead, and the streaming window.
Unknown or live duration is especially important. A disk destination has much more capacity than a browser tab, but that does not make an unbounded stream safe. Without an authoritative duration and a bounded workload, the correct action is to reject the job rather than turn direct saving into an unlimited task.
Network access is another independent constraint. A stream can fit in memory and still fail because its playlist or segments are unavailable to the browser, require credentials, or do not allow cross-origin access. Memory planning should not blur those failures into one generic export error.
Plan before opening the save picker
A tempting implementation asks the user where to save first and checks memory later. That creates a poor contract: the browser opens a privileged picker, may create a writable destination, and only then discovers that decoded frames or PCM cannot fit.
ArtPlayer uses the opposite order:
- Inspect the input and resolve the execution path.
- Build a buffered-output memory plan.
- If that plan fits, use the ordinary result flow.
- If it does not fit, build the streaming plan.
- If the streaming plan also exceeds the budget, stop before opening a picker.
- Only request a destination when streaming makes the complete task feasible.
This matters because direct saving is not a fallback after an out-of-memory crash. It is a different execution plan selected before expensive resources are allocated.
It also makes cancellation easier to reason about. Closing the picker is a user cancellation, not a processing failure. If a writable stream was opened but the job cannot continue, the pipeline should abort or close it deliberately rather than leaving an ambiguous partial result.
Why the input file size is a weak shortcut
Rules such as “allow files up to 1 GB” are easy to explain and often wrong.
A large, long, low-resolution source may be cheap to remux. A much smaller 4K clip may require several large decoded surfaces. A compressed audio file can expand dramatically when decoded to PCM. A generated video can have almost no source file while still requiring canvases, audio, an encoder, and a large result.
A useful preflight starts with the task architecture:
- Is the source file-backed or copied into memory?
- Is the video passed through, decoded one frame at a time, or retained?
- Is audio copied, block-decoded, or materialized as whole-track PCM?
- How many canvas or frame surfaces are live together?
- Is the final output buffered, streamed, or stored in MEMFS?
- Which runtime and safety reserves remain necessary?
The answer should produce both an estimated requirement and an explicit dominant component: output, PCM, canvas, retained frames, model runtime, or FFmpeg. That diagnosis leads to better guidance than simply telling every user to choose a smaller file.
What users can do when an export is too large
If a tool offers direct saving, use it when prompted and keep the destination available until processing finishes. Direct saving is most helpful when the encoded result is the dominant allocation.
If the streaming plan is still too large:
- Shorten the source before applying the expensive transformation.
- Reduce output resolution or frame rate when the tool exposes those controls.
- Avoid unnecessary audio processing when passthrough or muted output meets the goal.
- Close other media-heavy tabs and applications before retrying.
- Split a long job into bounded sections, then combine the results if the workflow allows it.
- Use a desktop editor when the operation inherently needs whole-track PCM, many retained frames, or FFmpeg files larger than the browser can safely hold.
These are workload changes, not rituals. Reloading a tab may clear leaked or fragmented state, but it does not make an intrinsically oversized plan feasible.
How to test a direct-save implementation
A download appearing in the file system is not enough. A useful browser acceptance test should verify the complete result:
- Process a representative input through the real execution path.
- Confirm that the saved file grows during processing when the platform exposes that behavior.
- Play the finished result in the browser.
- Open the downloaded file outside the page.
- Inspect duration, tracks, codec, dimensions, and expected edits.
- Cancel the picker and confirm that the UI returns to a usable state.
- Force a processing failure and check that partial writable state is handled deliberately.
- Test a workload where buffered output fails preflight but streamed output fits.
- Test another workload where even the streaming plan is rejected before the picker opens.
Memory measurements also need context. Record the browser, operating system, hardware, source properties, selected settings, and whether the measurement includes GPU or WebAssembly memory. A single JavaScript heap graph cannot prove the peak memory of the complete media pipeline.
The engineering lesson
Direct-to-disk export is valuable because it removes a specific, often large allocation: the complete encoded output and its overlapping browser representations. It can turn an otherwise impossible export into a practical one.
It is not a universal escape hatch.
Reliable browser media processing comes from modeling the whole task before it begins, choosing a bounded execution path, and being honest when the remaining decoded media or runtime state still exceeds the device’s safe capacity. The save picker is the final step in that decision—not the memory plan itself.