Modules โ
Movi-Player is designed with modularity in mind. Use only what you need.
Module Overview โ
| Module | Import Path | Size | Gzip | Brotli | Use Case |
|---|---|---|---|---|---|
| Demuxer | movi-player/demuxer | ~45KB | 2.31 MB | 1.74 MB | Metadata, HDR detection, packet reading |
| Player | movi-player/player | ~180KB | 2.52 MB | 1.91 MB | Playback control, custom UI |
| Element | movi-player / movi-player/element | ~410KB | 2.57 MB | 1.95 MB | Full UI player (drop-in) |
| Element (slim) | movi-player/element/slim | ~410KB | 1.00 MB + WASM | 750 KB + WASM | Same player, WASM as a separate file |
Module sizes (first column) exclude the embedded WASM binary. Gzip/Brotli columns show the total transfer size including WASM. Enable Brotli compression on your server for optimal delivery.
Demuxer Module (~45KB) โ
Use when you only need metadata extraction without playback.
import { Demuxer, HttpSource, FileSource } from "movi-player/demuxer";Exports โ
| Export | Type | Description |
|---|---|---|
Demuxer | Class | Media file demuxer |
HttpSource | Class | HTTP source adapter |
FileSource | Class | Local file source adapter |
ThumbnailHttpSource | Class | Optimized for thumbnails |
MoviVideoDecoder | Class | WebCodecs video decoder |
MoviAudioDecoder | Class | WebCodecs audio decoder |
SubtitleDecoder | Class | Subtitle parser |
SoftwareVideoDecoder | Class | Fallback video decoder |
SoftwareAudioDecoder | Class | Fallback audio decoder |
CodecParser | Class | Codec string utilities |
WasmBindings | Class | Low-level FFmpeg bindings |
LRUCache | Class | Cache implementation |
Logger, LogLevel | Utility | Logging |
Time, TIME_BASE | Utility | Time conversions |
EventEmitter | Class | Event handling |
Use Cases โ
- โ Extract video metadata (duration, resolution, codec)
- โ Detect HDR content (colorPrimaries, colorTransfer)
- โ List audio/subtitle tracks with languages
- โ Video file validation
- โ Read packets for custom processing
- โ No playback
- โ No rendering
Example โ
import { Demuxer, HttpSource } from "movi-player/demuxer";
async function getVideoInfo(url: string) {
const source = new HttpSource(url);
const demuxer = new Demuxer(source);
const info = await demuxer.open();
const video = demuxer.getVideoTracks()[0];
const audio = demuxer.getAudioTracks();
const subtitles = demuxer.getSubtitleTracks();
const result = {
format: info.formatName,
duration: info.duration,
video: video
? {
codec: video.codec,
resolution: `${video.width}x${video.height}`,
frameRate: video.frameRate,
isHDR: video.isHDR,
}
: null,
audioTracks: audio.length,
subtitleTracks: subtitles.length,
};
demuxer.close();
return result;
}Player Module (~180KB) โ
Use when you need full playback control without the built-in UI.

import { MoviPlayer, LogLevel } from "movi-player/player";Exports โ
Includes everything from demuxer module, plus:
| Export | Type | Description |
|---|---|---|
MoviPlayer | Class | Main player class |
CanvasRenderer | Class | WebGL2 canvas renderer |
AudioRenderer | Class | Web Audio renderer |
TrackManager | Class | Track management |
Clock | Class | A/V sync clock |
PlayerStateManager | Class | State machine |
PlaybackController | Class | Playback orchestration |
Use Cases โ
- โ Build custom player UI
- โ Canvas-based video rendering
- โ Thumbnail/preview generation
- โ Multi-player instances
- โ Headless video processing
- โ Frame-by-frame control
- โ No built-in controls
Example โ
import { MoviPlayer, LogLevel } from "movi-player/player";
MoviPlayer.setLogLevel(LogLevel.ERROR);
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
const player = new MoviPlayer({
source: {
type: "url",
url: "video.mp4",
},
canvas: canvas,
renderer: "canvas",
decoder: "auto",
});
// Event listeners
player.on("loadEnd", () => console.log("Ready!"));
player.on("stateChange", (state) => updateUI(state));
player.on("error", (e) => console.error(e));
// Load and control
await player.load();
await player.play();
// Build your own controls
document.getElementById("playBtn").onclick = () => player.play();
document.getElementById("pauseBtn").onclick = () => player.pause();
document.getElementById("seekBar").oninput = (e) => {
player.seek(parseFloat(e.target.value));
};Element Module (~410KB) โ
Complete drop-in video player with built-in UI.
import "movi-player";Exports โ
Registers the <movi-player> custom element. Includes everything from player module, plus:
- Custom Element (Web Component)
- Built-in controls UI
- Touch/mouse gestures
- Context menu
- Keyboard shortcuts
- Theming support
- Auto-scaling
Use Cases โ
- โ
Drop-in
<video>replacement - โ Zero configuration
- โ Built-in controls
- โ Mobile-friendly gestures
- โ Keyboard navigation
- โ Quick prototypes
Example โ
<script type="module">
import "movi-player";
</script>
<movi-player
src="video.mp4"
controls
autoplay
muted
style="width: 100%; height: 500px;"
></movi-player>Slim Element Module โ
The same <movi-player> element and the exact same API as movi-player/element โ only how the WASM ships differs. The default build embeds the FFmpeg WASM inside the JS (11.4 MB of JS); the slim build keeps it as a separate movi.wasm (4.2 MB JS + 5.6 MB WASM), so the engine streams and compiles as a cacheable asset instead of a base64 blob, and the JS parses far faster.
import "movi-player/element/slim";Two things to know:
- You must host
movi.wasmyourself. It ships in the package atmovi-player/dist/movi.wasm. Bundlers that understandnew URL("movi.wasm", import.meta.url)(Vite, webpack 5, Parcel 2) emit it automatically; otherwise copy it next to your JS bundle, or point at it withwasmurl. - Native fallback is automatic. Because a consumer may not host the extra
.wasm, a source the WASM engine can't open falls through to the browser's own<video>(wrapped in the player's controls) with nofallback="native"attribute needed. HLS/DASH play via Shaka without the WASM regardless.
<script type="module">
import "movi-player/element/slim";
</script>
<!-- movi.wasm hosted somewhere else? point at it -->
<movi-player
src="video.mkv"
wasmurl="https://cdn.example.com/movi-player/movi.wasm"
controls
></movi-player>With a framework wrapper โ
Every wrapper has a /slim twin โ same components, same props, same events:
| Framework | Default | Slim |
|---|---|---|
| React | movi-player/react | movi-player/react/slim |
| Vue 3 | movi-player/vue | movi-player/vue/slim |
| Svelte | movi-player/svelte | movi-player/svelte/slim |
import { MoviPlayer } from "movi-player/react/slim";
<MoviPlayer src="video.mkv" controls wasmurl="/movi.wasm" />;<script setup>
import { MoviPlayer } from "movi-player/vue/slim";
</script>
<template>
<MoviPlayer src="video.mkv" controls wasmurl="/movi.wasm" />
</template><script>
import MoviPlayer from "movi-player/svelte/slim";
</script>
<MoviPlayer src="video.mkv" controls wasmurl="/movi.wasm" />Import one or the other, not both: whichever loads second finds <movi-player> already defined, so it registers nothing while still shipping its own copy of the engine.
Module Composition โ
demuxer (~45KB)
โโโ FFmpeg WASM bindings
โโโ Container parsing (MP4, MKV, WebM, etc.)
โโโ Metadata extraction
โโโ Track enumeration
โโโ HDR detection
โโโ Packet reading
โโโ Source adapters (HTTP, File)
โโโ Codec parsers
player (~180KB) = demuxer +
โโโ MoviPlayer orchestrator
โโโ WebCodecs video decoder
โโโ WebCodecs audio decoder
โโโ Software decoder fallbacks
โโโ CanvasRenderer (WebGL2)
โโโ AudioRenderer (Web Audio)
โโโ TrackManager
โโโ Clock (A/V sync)
โโโ PlaybackController
โโโ State management
element (~410KB) = player +
โโโ <movi-player> Custom Element
โโโ Controls UI (play, seek, volume, fullscreen)
โโโ Touch gesture handler
โโโ Mouse gesture handler
โโโ Context menu
โโโ Settings panel
โโโ Theming engine
โโโ Keyboard shortcutsTree Shaking โ
Movi-Player supports tree shaking. Unused exports are removed:
// Only imports Demuxer and HttpSource
// ~45KB after tree shaking
import { Demuxer, HttpSource } from "movi-player/demuxer";
// Only what you use is bundled
const demuxer = new Demuxer(new HttpSource(url));CDN Usage โ
Use specific modules via CDN:
<!-- Full element (recommended for quick start) -->
<script type="module">
import "https://cdn.jsdelivr.net/npm/movi-player/dist/element.js";
</script>
<!-- Slim element: WASM streams from movi.wasm next to the JS -->
<script type="module">
import "https://cdn.jsdelivr.net/npm/movi-player/dist/element.slim.js";
</script>
<!-- Player only (for custom UI) -->
<script type="module">
import { MoviPlayer } from "https://cdn.jsdelivr.net/npm/movi-player/dist/player.js";
</script>
<!-- Demuxer only (for metadata) -->
<script type="module">
import {
Demuxer,
HttpSource,
} from "https://cdn.jsdelivr.net/npm/movi-player/dist/demuxer.js";
</script>Decision Guide โ
flowchart TD
A[What do you need?] --> B{Video playback?}
B -->|No| C[demuxer ~45KB]
B -->|Yes| D{Custom UI?}
D -->|Yes| E[player ~180KB]
D -->|No| F[element ~410KB]
C --> G[Metadata, HDR detection, validation]
E --> H[Build your own controls]
F --> I[Drop-in video player]| Scenario | Recommended Module |
|---|---|
| Video upload validation | demuxer |
| HDR detection service | demuxer |
| Video metadata API | demuxer |
| Custom player UI | player |
| Video editing timeline | player |
| Video thumbnail generation | player |
| Simple video playback | element |
| Quick prototype | element |
| Streaming platform | element or player |
Combining Modules โ
You can import from multiple modules, but avoid duplicates:
// โ
Good - import from most specific module
import { Demuxer, HttpSource } from "movi-player/demuxer";
import { MoviPlayer } from "movi-player/player";
// โ Avoid - don't import same thing from different paths
// import { Demuxer } from 'movi-player/demuxer';
// import { Demuxer } from 'movi-player/player'; // Same class, duplicatedPre-check Before Playback โ
import { Demuxer, HttpSource } from "movi-player/demuxer";
import { MoviPlayer } from "movi-player/player";
// Quick metadata check with demuxer
async function isValidVideo(url: string): Promise<boolean> {
const demuxer = new Demuxer(new HttpSource(url));
try {
await demuxer.open();
const hasVideo = demuxer.getVideoTracks().length > 0;
demuxer.close();
return hasVideo;
} catch {
return false;
}
}
// Full playback with player
async function play(url: string, canvas: HTMLCanvasElement) {
if (!(await isValidVideo(url))) {
throw new Error("Invalid or unsupported video");
}
const player = new MoviPlayer({
source: { type: "url", url },
canvas,
});
await player.load();
await player.play();
return player;
}