Skip to main content

Overview

Alpha channels add transparency support to video, enabling:
  • Transparent video overlays (logos, graphics, UI elements)
  • Green screen alternatives (pre-keyed footage)
  • Animated stickers and GIFs with transparency
  • Compositing workflows without chroma keying
node-webcodecs supports alpha channel encoding with VP8 and VP9 codecs.

Codec Support

VP9

Recommended for Alpha
  • Better compression than VP8
  • YUVA420P format
  • Widely supported
  • WebM container

VP8

Legacy Alpha Support
  • Good compression
  • YUVA420P format
  • Older browsers
  • WebM container

H.264

No Alpha Support
  • YUV only
  • No transparency

HEVC

Limited Alpha Support
  • Not widely supported
  • Proprietary extensions
Browser PlaybackAlpha video can be played in browsers using:
  • <video> tag with WebM container
  • Canvas API for compositing
  • WebGL for advanced effects

Quick Start

const { VideoEncoder, VideoFrame } = require('node-webcodecs');

const encoder = new VideoEncoder({
  output: (chunk, metadata) => {
    // Handle encoded alpha video chunk
  },
  error: (err) => console.error(err)
});

// Configure for alpha channel
encoder.configure({
  codec: 'vp09.00.10.08',  // VP9
  width: 1920,
  height: 1080,
  bitrate: 5_000_000,
  alpha: 'keep'  // ← Preserve alpha channel
});

// Encode RGBA frames
const data = new Uint8Array(1920 * 1080 * 4);
// ... fill with RGBA data (A = transparency) ...

const frame = new VideoFrame(data, {
  format: 'RGBA',
  codedWidth: 1920,
  codedHeight: 1080,
  timestamp: 0
});

encoder.encode(frame);
frame.close();

Alpha Modes

Keep Alpha (alpha: 'keep')

Preserves transparency in the encoded video:
encoder.configure({
  codec: 'vp09.00.10.08',
  width: 1920,
  height: 1080,
  alpha: 'keep'  // ← Preserve alpha
});
Use cases:
  • Transparent overlays
  • UI elements
  • Animated stickers
  • Any content with transparency

Discard Alpha (alpha: 'discard')

Removes alpha channel, treats it as fully opaque:
encoder.configure({
  codec: 'vp09.00.10.08',
  width: 1920,
  height: 1080,
  alpha: 'discard'  // ← Remove alpha (default)
});
Use cases:
  • Converting RGBA to RGB
  • When transparency isn’t needed
  • Smaller file sizes
Default BehaviorIf alpha is not specified, it defaults to 'discard'. Always set alpha: 'keep' explicitly for transparent video.

Complete Example

const { VideoEncoder, VideoFrame } = require('node-webcodecs');
const fs = require('fs');

async function encodeAlphaVideo() {
  const chunks = [];

  const encoder = new VideoEncoder({
    output: (chunk, metadata) => {
      const buffer = Buffer.alloc(chunk.byteLength);
      chunk.copyTo(buffer);
      chunks.push(buffer);

      if (metadata?.decoderConfig) {
        console.log('Encoder config:', metadata.decoderConfig);
      }
    },
    error: (err) => { throw err; }
  });

  // VP9 with alpha channel
  encoder.configure({
    codec: 'vp09.00.10.08',
    width: 640,
    height: 480,
    bitrate: 2_000_000,
    framerate: 30,
    alpha: 'keep'  // Preserve transparency
  });

  console.log('Encoding 60 frames with alpha channel...');

  // Create frames with circular transparency
  for (let i = 0; i < 60; i++) {
    const data = createAlphaFrame(640, 480, i);

    const frame = new VideoFrame(data, {
      format: 'RGBA',
      codedWidth: 640,
      codedHeight: 480,
      timestamp: i * 33333
    });

    encoder.encode(frame, { keyFrame: i % 30 === 0 });
    frame.close();
  }

  await encoder.flush();
  encoder.close();

  // Save to file
  const output = Buffer.concat(chunks);
  fs.writeFileSync('alpha_video.vp9', output);

  console.log(`Encoded ${chunks.length} chunks with alpha`);
  console.log(`File size: ${output.length} bytes`);
}

function createAlphaFrame(width, height, frameNum) {
  const data = new Uint8Array(width * height * 4);
  const centerX = width / 2;
  const centerY = height / 2;
  const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const idx = (y * width + x) * 4;

      // Calculate distance from center
      const dx = x - centerX;
      const dy = y - centerY;
      const dist = Math.sqrt(dx * dx + dy * dy);

      // Color gradient
      const hue = (frameNum / 60) * 360;
      data[idx] = Math.floor((x / width) * 255);       // R
      data[idx + 1] = Math.floor((y / height) * 255);  // G
      data[idx + 2] = Math.floor(hue % 256);           // B

      // Alpha: fade from opaque (center) to transparent (edges)
      data[idx + 3] = Math.max(0, 255 - Math.floor((dist / maxDist) * 255));
    }
  }

  return data;
}

encodeAlphaVideo().catch(console.error);
Output:
Encoding 60 frames with alpha channel...
Encoder config: { codec: 'vp09.00.10.08', ... }
Encoded 60 chunks with alpha
File size: 125890 bytes

Creating Alpha Frames

From RGBA Data

// Create RGBA buffer
const width = 1920;
const height = 1080;
const data = new Uint8Array(width * height * 4);

for (let i = 0; i < data.length; i += 4) {
  data[i] = 255;     // Red
  data[i + 1] = 0;   // Green
  data[i + 2] = 0;   // Blue
  data[i + 3] = 128; // Alpha (50% transparent)
}

const frame = new VideoFrame(data, {
  format: 'RGBA',
  codedWidth: width,
  codedHeight: height,
  timestamp: 0
});

Circular Gradient Alpha

function createCircularAlpha(width, height) {
  const data = new Uint8Array(width * height * 4);
  const centerX = width / 2;
  const centerY = height / 2;
  const radius = Math.min(centerX, centerY);

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const idx = (y * width + x) * 4;
      const dx = x - centerX;
      const dy = y - centerY;
      const dist = Math.sqrt(dx * dx + dy * dy);

      // Solid color
      data[idx] = 255;      // R
      data[idx + 1] = 255;  // G
      data[idx + 2] = 255;  // B

      // Circular alpha (opaque in center, transparent outside)
      if (dist < radius) {
        data[idx + 3] = 255;
      } else {
        data[idx + 3] = Math.max(0, 255 - (dist - radius));
      }
    }
  }

  return data;
}

Feathered Edge Alpha

function createFeatheredAlpha(width, height, featherSize = 50) {
  const data = new Uint8Array(width * height * 4);

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const idx = (y * width + x) * 4;

      // Color
      data[idx] = 255;
      data[idx + 1] = 0;
      data[idx + 2] = 0;

      // Feathered edges
      const distFromEdge = Math.min(x, y, width - x - 1, height - y - 1);
      const alpha = Math.min(255, (distFromEdge / featherSize) * 255);

      data[idx + 3] = alpha;
    }
  }

  return data;
}

VP8 vs VP9 for Alpha

Bitrate Recommendations

Alpha channels increase bitrate requirements:
// Without alpha (VP9)
bitrate: 5_000_000  // 5 Mbps for 1080p

// With alpha (VP9)
bitrate: 7_000_000  // 7 Mbps for 1080p (40% increase)
Recommended Bitrates with Alpha:
ResolutionVP9 with AlphaVP8 with Alpha
480p1.5 Mbps2 Mbps
720p3 Mbps4 Mbps
1080p7 Mbps9 Mbps
1440p12 Mbps16 Mbps
4K25 Mbps35 Mbps

Verifying Alpha in Output

Check if alpha channel is preserved:
ffprobe -show_streams alpha_video.vp9
Look for:
pix_fmt=yuva420p  # ✓ Alpha preserved (YUVA)
pix_fmt=yuv420p   # ✗ Alpha discarded (YUV)

Browser Playback

HTML5 Video

<!-- Alpha video in HTML -->
<video controls>
  <source src="alpha_video.webm" type="video/webm; codecs=vp9">
</video>

<style>
  video {
    background: linear-gradient(45deg, #ccc 25%, transparent 25%,
                transparent 75%, #ccc 75%, #ccc),
                linear-gradient(45deg, #ccc 25%, transparent 25%,
                transparent 75%, #ccc 75%, #ccc);
    background-size: 20px 20px;
    background-position: 0 0, 10px 10px;
  }
</style>

Canvas Compositing

const video = document.querySelector('video');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

function drawFrame() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw background
  ctx.fillStyle = 'blue';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Draw alpha video on top
  ctx.drawImage(video, 0, 0);

  requestAnimationFrame(drawFrame);
}

video.addEventListener('play', drawFrame);

Common Use Cases

Logo Overlay

// Encode transparent logo animation
encoder.configure({
  codec: 'vp09.00.10.08',
  width: 200,   // Small logo
  height: 100,
  bitrate: 500_000,
  alpha: 'keep'
});

// Position logo in corner during playback

Animated Stickers

// Create animated sticker with transparency
encoder.configure({
  codec: 'vp09.00.10.08',
  width: 512,
  height: 512,
  framerate: 30,
  bitrate: 1_500_000,
  alpha: 'keep'
});

// Perfect for social media, messaging apps

UI Elements

// Encode transparent UI animations
encoder.configure({
  codec: 'vp09.00.10.08',
  width: 1920,
  height: 200,  // Thin banner
  bitrate: 1_000_000,
  alpha: 'keep'
});

// Overlay on live video or game footage

Performance Considerations

Alpha encoding is more demanding than RGB encoding:
// Encoding time comparison (1080p, 60 frames)
// VP9 RGB:   ~500ms
// VP9 RGBA:  ~750ms (50% slower)

// File size comparison
// VP9 RGB:   ~2 MB
// VP9 RGBA:  ~2.8 MB (40% larger)
Optimization tips:
  • Use lower resolution for alpha elements
  • Increase compression with lower bitrate
  • Consider pre-rendering static transparency

Common Issues

Issue: Alpha not preserved

// ❌ Alpha discarded (default behavior)
encoder.configure({
  codec: 'vp09.00.10.08',
  // Missing alpha: 'keep'
});

// ✅ Alpha preserved
encoder.configure({
  codec: 'vp09.00.10.08',
  alpha: 'keep'
});

Issue: Poor alpha quality (blocky edges)

Solution: Increase bitrate for alpha channel:
encoder.configure({
  codec: 'vp09.00.10.08',
  bitrate: 7_000_000,  // ← Higher bitrate for clean alpha
  alpha: 'keep'
});

Issue: Large file sizes

Solution: Use VP9 instead of VP8, or reduce resolution:
// Instead of:
codec: 'vp8'  // Larger files

// Use:
codec: 'vp09.00.10.08'  // 30-40% smaller

Best Practices

encoder.configure({
  codec: 'vp09.00.10.08',
  alpha: 'keep'  // ← Don't forget this!
});
The default is 'discard', which removes transparency.
VP9 provides 30-40% better compression than VP8 for alpha video.
codec: 'vp09.00.10.08'  // Recommended
Alpha channels need ~40% more bitrate for equivalent quality:
// RGB: 5 Mbps
// RGBA: 7 Mbps
Always verify alpha quality on different backgrounds:
  • Solid colors
  • Gradients
  • Checkerboard patterns
  • Real footage

Next Steps