FFmpeg is best known as a native command-line tool for video conversion, compression, cutting, filtering, and encoding. Today, builds compiled to WebAssembly can perform a substantial part of that work inside the browser, including reading selected media, running filter graphs, re-encoding, and generating MP4 output.

But 'FFmpeg runs in the browser' does not mean it runs under the same conditions as native FFmpeg. The difference between single-thread and multi-thread builds affects more than CPU usage: SharedArrayBuffer, Web Workers, cross-origin isolation, and HTTP response headers all become part of the design. Browser Kitty's FFmpeg Filter Builder therefore keeps a portable single-thread build and a multi-thread build intended for HTTP(S) delivery.

Five pieces behind ST and MT FFmpeg in the browser

WebAssembly

Bring FFmpeg into the browser

FFmpeg source code is compiled for WebAssembly and runs inside the browser sandbox. The build configuration decides which codecs, filters, and optional libraries are included.

Worker

Keep heavy work off the UI thread

Even a single-thread FFmpeg WASM build can run in a Web Worker. 'Single-thread' does not mean the whole web page is limited to one browser thread.

SharedArrayBuffer

Share memory across workers

A multi-thread build uses SharedArrayBuffer so multiple workers can access and synchronize on shared WebAssembly memory. This is a core piece of Emscripten's Pthreads model.

Isolation

MT changes delivery requirements

SharedArrayBuffer on normal web pages requires cross-origin isolation. Because COOP and COEP are HTTP response headers, an MT build has different delivery requirements from a portable ST HTML opened directly from disk.

Trade-off

Look beyond raw speed

Multi-threading can use more CPU resources, but it also adds workers, synchronization, shared memory, and hosting requirements. The speedup varies by workload, so ST and MT should be chosen for the deployment and task.

The browser is running a WebAssembly build of FFmpeg, not a built-in FFmpeg command

Chrome or Edge is not exposing the native ffmpeg command directly. FFmpeg source code is compiled to WebAssembly with a toolchain such as Emscripten, and JavaScript manages that runtime. Because a browser cannot freely read arbitrary paths on the user's computer, media is passed in through user-approved browser file APIs instead.

Compiling to WebAssembly also does not automatically include every FFmpeg feature. Decoders, encoders, muxers, filters, and optional libraries are selected at build time, which affects both WASM size and capability. A command that works in a desktop FFmpeg build therefore is not guaranteed to exist in a browser build.

A simplified browser FFmpeg path
FFmpeg source Emscripten WebAssembly Browser / Worker

FFmpeg still runs under browser sandbox and file-access rules

Single-thread FFmpeg can still run away from the UI main thread

The name single-thread can sound as though the entire page, including its UI, must run on one thread. That is not necessary. FFmpeg WASM itself can run inside a Web Worker, separating heavy media work from the main thread that manages the interface and returning progress messages back to the page.

It is more useful to think of single-thread here as an FFmpeg runtime that does not use multiple Pthreads. The ST build also has a concrete advantage: it can avoid SharedArrayBuffer and cross-origin-isolation requirements, which makes distribution much simpler.

SharedArrayBuffer is at the center of the multi-thread build

Multiple FFmpeg threads need more than independent worker copies. The workers need access to the same WebAssembly memory and a way to synchronize. Emscripten's Pthreads implementation therefore uses SharedArrayBuffer.

SharedArrayBuffer lets multiple workers reference shared memory, with Atomics providing synchronization primitives. This makes a browser execution model closer to native multithreading possible, but workers and synchronization have costs of their own, so increasing the thread count does not produce linear speedups.

SharedArrayBuffer changes how the HTML has to be delivered

On current major browsers, the normal way to expose SharedArrayBuffer to a web page requires a secure context and cross-origin isolation. A common configuration uses Cross-Origin-Opener-Policy: same-origin together with Cross-Origin-Embedder-Policy: require-corp (or credentialless) as HTTP response headers. The page can inspect the resulting state through crossOriginIsolated.

This is a major difference from the ST build. An HTML file opened directly with file:// cannot receive those server response headers. Browser Kitty therefore keeps the standard FFmpeg Filter Builder build usable as a portable file:// HTML, while the MT build assumes HTTP(S) plus cross-origin isolation. Browser Kitty also sends Cross-Origin-Resource-Policy: same-origin for that app, but CORP is an additional restriction rather than the direct requirement that makes crossOriginIsolated true.

How Browser Kitty separates ST and MT
AspectSingle-threadMulti-thread
FFmpeg runtimeSingle-thread configurationUses multiple Pthreads
SharedArrayBufferNot requiredRequired
Deliveryfile:// or HTTP(S)HTTP(S) with cross-origin isolation
PortabilityEasy to open as a standalone HTMLNeeds server headers or another isolation mechanism
Main advantageCompatibility and simple distributionCan use CPU parallelism

Multi-thread does not mean automatically or proportionally faster

Video re-encoding and complex filter graphs can be CPU intensive, so an MT build can benefit from multiple cores. ffmpeg.wasm's published performance examples include workloads where the multi-thread build is faster than the single-thread build. Those numbers belong to specific versions, machines, and operations, however; they are not a promise that every workload scales by the same factor.

Some work parallelizes better than others, while worker synchronization, memory management, and thread startup add overhead. MT also tends to use more memory because it adds workers and shared runtime state. For browser video processing, practical limits can therefore come from device memory before raw CPU throughput.

Browser Kitty isolates only the app that needs MT FFmpeg

Cross-origin isolation is not necessarily something to apply to an entire site indiscriminately. COEP changes the conditions under which cross-origin images, iframes, scripts, and other resources can be embedded, which can affect ads and third-party content. Browser Kitty therefore applies COOP / COEP only to /apps/ffmpeg-filter-builder.html through an Azure Static Web Apps route, leaving other tools and portal pages unchanged.

The FFmpeg Filter Builder repository's GitHub Pages MT page has a same-origin coi-serviceworker fallback that loads only when crossOriginIsolated is false, for hosting where the required headers cannot be configured directly. Browser Kitty production does not depend on that fallback because the server response headers make the page cross-origin isolated from the start.

Browser FFmpeg is not a drop-in replacement for native FFmpeg

Browser FFmpeg is useful because it can run without installation, process selected media without the app uploading it to a server, and pair well with GUIs or predefined recipes. For a handful of local files, that can be more convenient than preparing a command-line environment.

Native FFmpeg remains better suited to large batch jobs, very long or high-resolution media, broad codec/library coverage, automation, and maximum throughput. WebAssembly does not turn the browser into the same execution environment as native FFmpeg; it brings a useful subset of FFmpeg into the browser's security and resource constraints.

A practical way to choose ST or MT
Need portable file://? CPU-heavy render? Can hosting set COOP / COEP? Choose ST / MT

Choose based on delivery, compatibility, and memory—not speed alone

Try it in Browser Kitty

FFmpeg Filter Builder

Build FFmpeg filter graphs with editable nodes, preview the result, and render H.264/AAC MP4 using the multi-thread build on Browser Kitty.

Open toolView tool details

Tips and limitations

  • A single-thread build can still run FFmpeg inside a worker, so ST does not automatically mean the UI must freeze. Separate the browser UI thread from FFmpeg's internal thread model.
  • Needing SharedArrayBuffer does not require isolating an entire site. Check the effect on third-party embeds and consider applying COOP / COEP only to the route that needs them.
  • Fully local processing is different from offline startup. An MT build may require HTTP(S) delivery while still processing selected video without uploading that video to an external server.
  • Watch memory as well as CPU for large video. MT can improve throughput while still hitting device-memory limits first.

Frequently asked questions

Does a single-thread build necessarily freeze the page while processing video?

Not necessarily. FFmpeg WASM can run in a Web Worker, separate from the main thread that manages the UI. Single-thread mainly describes the FFmpeg runtime not using multiple Pthreads.

If the browser supports SharedArrayBuffer, will the multi-thread build work everywhere?

Browser API support alone is not enough. The normal deployment also needs a secure context and cross-origin isolation, which means the HTTP(S) delivery environment must satisfy COOP / COEP conditions. It also cannot be blocked by Permissions-Policy: cross-origin-isolated; its default allowlist is self, so a typical top-level same-origin page normally needs no additional setting. Check both the browser and the hosting environment.

Is CORP required in addition to COOP and COEP?

The common direct isolation combination is COOP: same-origin plus COEP: require-corp or credentialless. CORP is a separate resource policy header. Browser Kitty also sends CORP: same-origin for FFmpeg Filter Builder as an additional restriction.

Is the multi-thread build always faster than the single-thread build?

No. CPU-heavy encoding can benefit, but some work parallelizes poorly and worker synchronization has overhead. The result varies with CPU cores, memory, media characteristics, and the filter graph.

Does HTTP(S) delivery mean the multi-thread build is no longer fully local processing?

No. Fetching the application HTML and processing the selected media are separate questions. Browser Kitty loads the app from the web, but the selected video, preview, and full render are processed in the browser rather than uploaded by the app to an external processing server.

References

The SharedArrayBuffer, cross-origin-isolation, Emscripten Pthreads, and FFmpeg WebAssembly discussion is based on the official and project documentation below. Browser Kitty-specific ST / MT delivery details come from the public FFmpeg Filter Builder repository.