
Security News
/Research
Popular node-ipc npm Package Infected with Credential Stealer
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.
webcodecs-encoder
Advanced tools
A TypeScript library for browser environments to encode video (H.264/AVC, VP9, VP8) and audio (AAC, Opus) using the WebCodecs API and mux them into MP4 or WebM containers with real-time streaming support. New function-first API design.
A TypeScript library to encode video (H.264/AVC, VP9, VP8) and audio (AAC, Opus) using the WebCodecs API and mux them into MP4 or WebM containers with a simple, function-first design.
π v1.0.0 Release
This is the stable release with the new function-first API. The API is now simplified and production-ready with automatic configuration, quality presets, and progressive enhancement.
encode(), encodeStream(), and canEncode() functionslow, medium, high, lossless presetsencodeStream()npm install webcodecs-encoder
# or
yarn add webcodecs-encoder
No additional setup required! The library automatically manages Web Workers internally.
import { encode } from 'webcodecs-encoder';
// Encode frames with automatic configuration
const frames = [/* VideoFrame, Canvas, ImageData objects */];
const mp4Data = await encode(frames, { quality: 'medium' });
// Save or use the encoded MP4
const blob = new Blob([mp4Data], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
import { encodeStream } from 'webcodecs-encoder';
// Real-time encoding for live streaming
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
for await (const chunk of encodeStream(stream, { quality: 'high' })) {
// Send chunk to MediaSource, server, or save incrementally
mediaSource.appendBuffer(chunk);
}
import { canEncode } from 'webcodecs-encoder';
// Check if encoding is supported
const isSupported = await canEncode();
// Check specific configuration
const canEncodeHEVC = await canEncode({
video: { codec: 'hevc' },
quality: 'high'
});
encode(source, options?)Encode video to a complete MP4/WebM file.
async function encode(
source: VideoSource,
options?: EncodeOptions
): Promise<Uint8Array>
encodeStream(source, options?)Stream encoding with real-time chunks.
async function* encodeStream(
source: VideoSource,
options?: EncodeOptions
): AsyncGenerator<Uint8Array>
canEncode(options?)Check if encoding is supported with given options.
async function canEncode(options?: EncodeOptions): Promise<boolean>
The API supports multiple input types:
type VideoSource =
| Frame[] // Static frame array
| AsyncIterable<Frame> // Dynamic frame generation
| MediaStream // Camera/screen capture
| VideoFile; // Existing video file
type Frame = VideoFrame | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | ImageData;
interface EncodeOptions {
// Basic settings (auto-detected if not specified)
width?: number;
height?: number;
frameRate?: number;
// Quality preset (recommended)
quality?: 'low' | 'medium' | 'high' | 'lossless';
// Advanced settings
video?: {
codec?: 'avc' | 'hevc' | 'vp9' | 'vp8' | 'av1';
bitrate?: number;
hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
latencyMode?: 'quality' | 'realtime';
keyFrameInterval?: number;
};
audio?: {
codec?: 'aac' | 'opus';
bitrate?: number;
sampleRate?: number;
channels?: number;
bitrateMode?: 'constant' | 'variable';
} | false; // false to disable audio
container?: 'mp4' | 'webm';
// Callbacks
onProgress?: (progress: ProgressInfo) => void;
onError?: (error: EncodeError) => void;
}
import { encode } from 'webcodecs-encoder';
// Create animation frames
const frames = [];
const canvas = new OffscreenCanvas(800, 600);
const ctx = canvas.getContext('2d');
for (let i = 0; i < 120; i++) { // 4 seconds at 30fps
ctx.clearRect(0, 0, 800, 600);
ctx.fillStyle = `hsl(${i * 3}, 70%, 50%)`;
ctx.fillRect(i * 6, 200, 100, 200);
frames.push(canvas.transferToImageBitmap());
}
// Encode with automatic settings
const mp4 = await encode(frames, {
quality: 'high',
frameRate: 30
});
// Save the file
const blob = new Blob([mp4], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'animation.mp4';
a.click();
import { encode } from 'webcodecs-encoder';
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 1280, height: 720 },
audio: true
});
const mp4 = await encode(stream, {
quality: 'medium',
container: 'mp4',
onProgress: (progress) => {
console.log(`Progress: ${progress.percent.toFixed(1)}%`);
console.log(`Speed: ${progress.fps.toFixed(1)} fps`);
if (progress.estimatedRemainingMs) {
console.log(`ETA: ${(progress.estimatedRemainingMs / 1000).toFixed(1)}s`);
}
}
});
import { encodeStream } from 'webcodecs-encoder';
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
const chunks = [];
for await (const chunk of encodeStream(stream, {
quality: 'medium',
video: { latencyMode: 'realtime' }
})) {
// Send to server or MediaSource immediately
chunks.push(chunk);
// Or stream to MediaSource Extensions
if (mediaSource.readyState === 'open') {
sourceBuffer.appendBuffer(chunk);
}
}
// Combine all chunks for final file
const fullVideo = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
fullVideo.set(chunk, offset);
offset += chunk.length;
}
import { encode } from 'webcodecs-encoder';
// Generate frames dynamically
async function* generateFrames() {
const canvas = new OffscreenCanvas(640, 480);
const ctx = canvas.getContext('2d');
for (let frame = 0; frame < 300; frame++) { // 10 seconds at 30fps
// Draw your animation
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, 640, 480);
ctx.fillStyle = '#fff';
ctx.font = '48px Arial';
ctx.fillText(`Frame ${frame}`, 50, 240);
yield canvas.transferToImageBitmap();
// Optional: add timing control
await new Promise(resolve => setTimeout(resolve, 33)); // ~30fps
}
}
const mp4 = await encode(generateFrames(), {
quality: 'high',
frameRate: 30
});
For repeated encoding with the same settings:
import { createEncoder, encoders } from 'webcodecs-encoder/factory';
// Create custom encoder
const myEncoder = createEncoder({
quality: 'high',
video: { codec: 'avc' },
audio: { codec: 'aac', bitrate: 192_000 }
});
// Use multiple times
const video1 = await myEncoder.encode(frames1);
const video2 = await myEncoder.encode(frames2);
// Or use predefined encoders
const youtubeVideo = await encoders.youtube.encode(frames);
const twitterVideo = await encoders.twitter.encode(frames);
import { examples } from 'webcodecs-encoder/factory';
// Optimize for specific platforms
const youtubeEncoder = examples.getEncoderForPlatform('youtube');
const twitterEncoder = examples.getEncoderForPlatform('twitter');
// Resolution-based optimization
const hdEncoder = examples.createByResolution(1920, 1080);
const mobileEncoder = examples.createByResolution(640, 480);
// File size constraints
const smallFileEncoder = examples.createForFileSize(10, 60); // 10MB for 60 seconds
import { encode, EncodeError } from 'webcodecs-encoder';
try {
const mp4 = await encode(frames, { quality: 'high' });
} catch (error) {
if (error instanceof EncodeError) {
switch (error.type) {
case 'not-supported':
console.log('WebCodecs not supported in this browser');
break;
case 'invalid-input':
console.log('Invalid input frames or configuration');
break;
case 'encoding-failed':
console.log('Encoding process failed:', error.message);
break;
default:
console.log('Unknown encoding error:', error.message);
}
}
}
dom.media.webcodecs.enabled)Check support at runtime:
import { canEncode } from 'webcodecs-encoder';
const supported = await canEncode();
if (!supported) {
// Fallback to MediaRecorder or other solutions
}
{ video: { hardwareAcceleration: 'prefer-hardware' } }encodeStream() instead of encode()MIT License - see LICENSE file for details.
Contributions are welcome! Please see our Contributing Guide for details.
FAQs
A TypeScript library for browser environments to encode video (H.264/AVC, VP9, VP8) and audio (AAC, Opus) using the WebCodecs API and mux them into MP4 or WebM containers with real-time streaming support. New function-first API design.
The npm package webcodecs-encoder receives a total of 14 weekly downloads. As such, webcodecs-encoder popularity was classified as not popular.
We found that webcodecs-encoder demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.

Security News
TeamPCP and BreachForums are promoting a Shai-Hulud supply chain attack contest with a $1,000 prize for the biggest package compromise.

Security News
Packagist urges PHP projects to update Composer after a GitHub token format change exposed some GitHub Actions tokens in CI logs.