Warning
This project is under active development and is not ready for production use.
mpv-prism lets Electron apps use libmpv for decoding, playback control, subtitles, shaders, profiles, and media handling, then presents frames through a regular DOM <video> or <canvas>. Where supported, frames are passed through shared GPU textures. When that's not possible, it falls back to the CPU.
- Real DOM video integration through a standard
<video>or<canvas>element. - Hardware-accelerated rendering with CPU fallback
- Playback controls for loading, play/pause, seeking, volume, speed, tracks, subtitles, etc.
- Typed playback, track, subtitle, error, and native events
- Runtime mpv profiles, shaders, properties, and raw commands
- Vanilla TypeScript core API with an optional React wrapper
- Runtime loading of the correct native addon and sidecar libraries
@mpv-prism/core
@mpv-prism/electron
@mpv-prism/react
- Electron 42+
contextIsolation: truenodeIntegration: falsesandbox: falseMediaStreamTrackGeneratorandVideoFramesupport in the renderer- Native artifacts in
native-builds/<platform>-<arch>/
Prepare the libmpv artifacts for each platform you want to support.
Initialize the IPC bridge in the Electron main process prior to creating application windows or spawning players:
import { app, BrowserWindow } from 'electron';
import { join } from 'node:path';
import { registerMpvPrismIpc, type MpvPrismMain } from '@mpv-prism/electron/main';
let mpvPrism: MpvPrismMain | null = null;
async function createWindow() {
// Register the IPC handlers once
if (!mpvPrism) {
mpvPrism = registerMpvPrismIpc({
loader: {
// Absolute base path to look for the native-builds/ directory
baseDirectory: join(__dirname, '..'),
},
});
}
const window = new BrowserWindow({
width: 1280,
height: 720,
webPreferences: {
preload: join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: false, // Required for ESM preload bridge
backgroundThrottling: false,
},
});
await window.loadFile('index.html');
}
app.whenReady().then(createWindow);
app.on('will-quit', () => {
mpvPrism?.dispose();
mpvPrism = null;
});To configure custom search paths for the native library loader, pass the folder option in loader.baseDirectory, or set the MPV_PRISM_NATIVE_ROOT environment variable.
Expose the preload bridge:
import { exposeMpvPrismPreload } from '@mpv-prism/electron/preload';
exposeMpvPrismPreload();The preload layer validates renderer input, forwards IPC commands, receives shared textures, creates VideoFrame targets, and releases frames when they are no longer needed.
Use @mpv-prism/core directly with vanilla TypeScript, JavaScript, or other UI frameworks.
import { MpvPrismPlayer } from '@mpv-prism/core';
// Create a player instance
const player = new MpvPrismPlayer({
mpv: {
options: {
'hwdec': 'auto-safe'
}
}
});
const video = document.querySelector('video') as HTMLVideoElement;
// Attach a video presenter
const presenter = player.createVideoPresenter({ fit: 'contain' });
await presenter.attach(video);
// Subscribe to state streams
const unsubscribe = player.on('position', (event) => {
console.log('Position update:', event.position);
});
// Control playback
await player.load('file:///path/to/media.mkv');
await player.play();
await player.seek(60, 'absolute+exact');
// Configure shaders and profiles
await player.setShaders(['/absolute/path/to/shader.glsl']);
await player.clearShaders();
// Cleanup
unsubscribe();
await presenter.destroy();
await player.destroy();| Variable | Platform | Allowed Values / Type | Description |
|---|---|---|---|
MPV_PRISM_HIGH_PERFORMANCE_GPU |
Windows, Linux | 1 / true / yes / on |
Prioritize the discrete high-performance GPU rather than integrated graphics. |
MPV_PRISM_DEBUG_VIDEO |
All | 1 / true / yes / on |
Enables detailed debug statements for the video presentation, queue levels, and rendering frame loop. |
MPV_PRISM_DEBUG_NATIVE |
All | 1 / true / yes / on |
Enables detailed native addon debugging in standard output. |
MPV_PRISM_MPV_LOG_FILE |
All | Absolute path | File path where logs will be written. |
MPV_PRISM_NATIVE_LOG_FILE |
All | Absolute path | File path where native runtime logs will be written. |
MPV_PRISM_NATIVE_ROOT |
All | Absolute path | Custom search directory override for resolving the native-builds/ target folders. |
MPV_PRISM_LIBMPV_PATH |
All | Absolute path | Instructs the dynamic native loader to import libmpv from a specific library binary directly. |
MPV_PRISM_WIN32_BACKEND |
Windows | auto, wgl, angle |
Explicitly overrides the graphics presentation backend. Defaults to auto. |
MPV_PRISM_WIN32_ANGLE_ADAPTER |
Windows | auto, default, high-performance |
Configures DXGI adapter preferences when using ANGLE on Windows. Defaults to auto. |
MPV_PRISM_ANGLE_DLL_DIR |
Windows | Absolute path | Folder path containing the ANGLE library dependencies (libEGL.dll, libGLESv2.dll). |
MPV_PRISM_WIN32_ALLOW_ES2 |
Windows | 1 / true / yes / on |
Allows ANGLE initialization fallback to OpenGL ES 2 contexts. |
MPV_PRISM_LINUX_GPU_PREFERENCE |
Linux | default, discrete, nvidia, amd, intel, compatible |
Prefers a specific vendor GPU node for offscreen EGL context creation. |
MPV_PRISM_DRM_DEVICE |
Linux | Absolute path | Explicit path to the DRM card node (e.g. /dev/dri/card0 or /dev/dri/renderD128). |
Exclude your prebuilt native folders from Electron's compressed ASAR container to allow dynamic loading of native addons and graphics libraries:
native-builds/<platform>-<arch>/*
In your build setup (e.g. electron-builder), configure extraResources or asarUnpack to keep the target binaries uncompressed:
"extraResources": [
{
"from": "native-builds",
"to": "native-builds",
"filter": ["**/*"]
}
]Keep your .node binaries directly alongside their required runtime dependencies (.dll, .dylib, .so) in their platform subdirectory. The native loader resolves paths from process.resourcesPath, unpacked directories, current execution roots, or MPV_PRISM_NATIVE_ROOT.
| Platform | Backend | Status |
|---|---|---|
darwin-arm64 |
IOSurface | Tested |
linux-x64 |
EGL, GBM, dma-buf, NativePixmap | Tested |
win32-x64 |
WGL, D3D11 shared NT handle | Tested |
- VideoCompositor-related issues
- Issues with dual-gpu setups
