
Research
/Security News
Popular Go Decimal Library Targeted by Long-Running Typosquat with DNS Backdoor
A long-running Go typosquat impersonated the popular shopspring/decimal library and used DNS TXT records to execute commands.
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.
🎉 v0.2.2 Release
Major updates: Real-time streaming support, audio-only encoding, VideoFile audio processing, and optimized transferable objects. See CHANGELOG for details.
encode(), encodeStream(), and canEncode() functionslow, medium, high, lossless presetsencodeStream()video: false option (v0.2.2)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()🚀 Major Features
encodeStream() MediaStream processing - no longer throws errorsvideo: false option support for pure audio encoding🔧 Improvements
video: false configurations🐛 Bug Fixes
encodeStream()📝 Documentation
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 23 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.

Research
/Security News
A long-running Go typosquat impersonated the popular shopspring/decimal library and used DNS TXT records to execute commands.

Research
Active npm supply chain attack compromises @antv packages in a fast-moving malicious publish wave tied to Mini Shai-Hulud.

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