Vue
Use wasmpeg in a Vue 3 app — decode a frame to a canvas in a single-file component, share the loaded module with a composable, probe metadata, and offload to a worker.
wasmpeg is framework-agnostic, so in Vue you call the same API from onMounted, a watcher,
or a composable. On a Vite-based Vue app the WASM is served automatically; on other bundlers
you may need to copy it into public/ (covered at the end).
Single-file component
Install:
npm install wasmpegDecode a frame in a component. Loading the module and opening the decoder both happen in
onMountedso they only run in the browser, never during SSR:<script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; import wasmpeg from 'wasmpeg'; const props = defineProps({ file: { type: Object, required: true } }); const canvas = ref(null); let dec; onMounted(async () => { await wasmpeg.load(); dec = await wasmpeg.decode(props.file); const frame = dec.nextFrame(); if (!frame) return; canvas.value.width = dec.width; canvas.value.height = dec.height; canvas.value .getContext('2d') .putImageData(new ImageData(frame, dec.width, dec.height), 0, 0); }); onBeforeUnmount(() => dec?.close()); </script> <template> <canvas ref="canvas" /> </template>
decode() takes the File or Blob straight from an input or drop event. Each
nextFrame() returns RGBA8 pixels as a Uint8ClampedArray sized width × height × 4, or
null once the stream ends.
onBeforeUnmount. Every open decoder holds a session and native
buffers; if the component unmounts mid-decode and you skip close(), those resources leak.React to a changing file
If the file prop can change while the component is mounted, watch it and re-open the
decoder, closing the previous one first:
<script setup>
const props = defineProps({ file: { type: Object, required: true } });
const canvas = ref(null);
let dec;
watch(
() => props.file,
async (file) => {
await wasmpeg.load();
dec?.close();
dec = await wasmpeg.decode(file);
const frame = dec.nextFrame();
if (!frame) return;
canvas.value.width = dec.width;
canvas.value.height = dec.height;
canvas.value
.getContext('2d')
.putImageData(new ImageData(frame, dec.width, dec.height), 0, 0);
},
{ immediate: true },
);
onBeforeUnmount(() => dec?.close());
</script>
<template>
<canvas ref="canvas" />
</template>
Reusable composable
Wrap loading in a composable so the module loads once and is shared across components. The
??= keeps the call idempotent — the first caller starts the load, everyone else awaits the
same promise:
// useWasmpeg.js
let ready;
export function useWasmpeg() {
ready ??= wasmpeg.load();
return { wasmpeg, ready };
}
<script setup>
const { wasmpeg, ready } = useWasmpeg();
async function inspect(file) {
await ready;
const info = await wasmpeg.probe(file);
console.log(info.format, info.duration, info.video.width, info.video.height);
}
</script>
probe() returns container format, duration in seconds (or null when unknown), bitrate,
the stream list, and video/audio details — without decoding any frames, so it’s cheap enough
to run on every selected file.
Offload decoding to a Worker
Decoding blocks the thread it runs on. For long clips, move it into a Web Worker so the UI keeps painting. The API is the same inside a worker:
// decode.worker.js
let ready;
self.onmessage = async (e) => {
ready ??= wasmpeg.load();
await ready;
const dec = await wasmpeg.decode(e.data.file);
try {
for (let frame; (frame = dec.nextFrame()); ) {
self.postMessage({ width: dec.width, height: dec.height, frame }, [frame.buffer]);
}
} finally {
dec.close();
self.postMessage({ done: true });
}
};
On Vite, instantiate it with new Worker(new URL('./decode.worker.js', import.meta.url), { type: 'module' }).
Transferring frame.buffer moves the pixels to the main thread without a copy.
If the WASM 404s
On a non-Vite bundler, or if cpu.wasm shows up as a 404, copy the binaries into public/
and pass an explicit path:
cp node_modules/wasmpeg/dist/cpu.* public/wasmpeg/
await wasmpeg.load({ wasmPath: '/wasmpeg/cpu.js' }); // cpu.wasm sits beside it
nextFrame() from requestAnimationFrame rather than a
tight loop, and always close() the decoder in onBeforeUnmount.