How to Clip Videos in the Browser with WebAssembly and FFmpeg
This article explains how to use WebAssembly and FFmpeg compiled to wasm for client‑side video clipping, covering the basics of WebAssembly, setting up Emscripten, loading ffmpeg.wasm, building a minimal Node server, and adding a slider UI for selecting clip ranges.
Introduction
Recently I read an article about WebAssembly and became curious about trying it out.
What is WebAssembly?
WebAssembly (wasm) is a portable, small‑size, fast‑loading binary format that runs on the web. C/C++ code can be compiled to a .wasm file, delivered to the browser, and invoked from JavaScript.
Advantages of WebAssembly
WebAssembly offers superior performance compared to JavaScript, making it ideal for compute‑intensive tasks such as image/video decoding, 3D/WebVR/AR, and other high‑performance scenarios. Existing C/C++ libraries can be compiled to wasm and used directly in the browser, reducing server load.
Simple WebAssembly Example
We start with a minimal C file:
<code>int add(int a, int b) {
return a + b;
}</code>Compile it with Emscripten:
<code>emcc test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o test.wasm</code>Then load it in HTML:
<code>fetch('./test.wasm')
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.instantiate(bytes))
.then(results => {
const add = results.instance.exports.add;
console.log(add(11, 33));
});</code>The console will show the result, confirming the wasm function works.
Getting Started
Having learned how to call compiled C/C++ code in the browser, we can move toward a practical goal: clipping video on the client side.
Demo Demonstration
The demo repository is https://github.com/Dseekers/clip-video-by-webassembly . Below is a screenshot of the demo.
FFmpeg
FFmpeg is an open‑source suite for handling audio and video formats, providing libraries such as libavcodec and libavformat.
FFmpeg can record, convert, and stream audio/video in many formats.
It is written in C and can be used via command‑line arguments. For example, to cut a segment:
<code>ffmpeg -ss [start] -i [input] -to [end] -c copy [output]</code>where
startand
enddefine the clip range,
inputis the source video, and
outputis the resulting file.
Obtaining FFmpeg WebAssembly
Compiling FFmpeg to wasm with Emscripten can be problematic, so we use a pre‑built CDN package from ffmpeg.wasm .
The package includes four files:
<code>ffmpeg.min.js
ffmpeg-core.js
ffmpeg-core.wasm
ffmpeg-core.worker.js</code>Only
ffmpeg.min.jsneeds to be imported; the other files are fetched automatically.
Minimal Implementation
Because ffmpeg.wasm requires
SharedArrayBuffer, we must set the following response headers on a simple Node server:
<code>Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp</code>Node server example (Koa):
<code>const Koa = require('koa');
const path = require('path');
const fs = require('fs');
const router = require('koa-router')();
const static = require('koa-static');
const staticPath = './static';
const app = new Koa();
app.use(static(path.join(__dirname, staticPath)));
app.use(async (ctx, next) => {
console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);
ctx.set('Cross-Origin-Opener-Policy', 'same-origin');
ctx.set('Cross-Origin-Embedder-Policy', 'require-corp');
await next();
});
router.get('/', async ctx => { ctx.body = '<h1>Index</h1>'; });
router.get('/:filename', async ctx => {
const filePath = path.join(__dirname, ctx.request.url);
const htmlContent = fs.readFileSync(filePath);
ctx.type = 'html';
ctx.body = htmlContent;
});
app.use(router.routes());
app.listen(3000);
console.log('app started at port 3000...');</code>Demo HTML loads the ffmpeg script and provides UI for selecting a video file and starting the clip:
<code><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script src="./assets/ffmpeg.min.js"></script>
<div class="container">
<div class="operate">
Select original video file:
<input type="file" id="select_origin_file">
<button id="start_clip">Start Clip</button>
</div>
<div class="video-container">
<div class="label">Original Video</div>
<video class="my-video" id="origin-video" controls></video>
</div>
<div class="video-container">
<div class="label">Processed Video</div>
<video class="my-video" id="handle-video" controls></video>
</div>
</div></code>JavaScript (jQuery) loads the selected file, creates an FFmpeg instance, runs the clipping command, and displays the result:
<code>let originFile;
$(document).ready(function() {
$('#select_origin_file').on('change', e => {
const file = e.target.files[0];
originFile = file;
const url = window.webkitURL.createObjectURL(file);
$('#origin-video').attr('src', url);
});
$('#start_clip').on('click', async function() {
const { fetchFile, createFFmpeg } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true, corePath: './assets/ffmpeg-core.js' });
const { name } = originFile;
if (!ffmpeg.isLoaded()) await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(originFile));
await ffmpeg.run('-i', name, '-ss', '00:00:00', '-to', '00:00:01', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const tempURL = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
$('#handle-video').attr('src', tempURL);
});
});</code>The core functionality is now complete.
Small Optimizations
To allow users to choose arbitrary start and end times, a slider UI (Vue) is added. Below is the core ClipVideo class (JS) that handles the slider, time conversion, and FFmpeg execution.
<code>class ClipVideo {
constructor() {
this.ffmpeg = null;
this.originFile = null;
this.handleFile = null;
this.vueInstance = null;
this.currentSliderValue = [0, 0];
this.init();
}
init() {
console.log('init');
this.initFfmpeg();
this.bindSelectOriginFile();
this.bindOriginVideoLoad();
this.bindClipBtn();
this.initVueSlider();
}
// ... (methods omitted for brevity) ...
}
$(document).ready(function() {
const instance = new ClipVideo();
});</code>With these additions, the article demonstrates a functional front‑end video clipping tool powered by WebAssembly.
Conclusion
WebAssembly is still a relatively new technology; this article only scratches its surface by implementing a simple video clipping demo. There is much more to explore.
References
WebAssembly 完全入门——了解 wasm 的前世今生 (https://juejin.cn/post/6844903709806182413)
使用 FFmpeg 与 WebAssembly 实现纯前端视频截帧 (https://toutiao.io/posts/7as4kva/preview)
前端视频帧提取 ffmpeg + Webassembly (https://juejin.cn/post/6854573219454844935)
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.