🚧 Work in progress, use with caution in production.
A react component for stacked-alpha video.
The stacked-alpha video rendering technique is based on Jake Archibald's research: article and his web component repo. This approach provides high compression efficiency while preserving excellent visual quality for transparent videos.
Enhanced for React.
- Optimized for React - Compatiable with React's ref and state management.
- Automatic canvas resizing - Canvas automatically resizes to match its display size.
- HIDPI/Retina support - Automatically detects and adapts to high-DPI displays for crisp rendering.
- Automatic source refresh - Video automatically reloads when source props change.
- Object-fit support - Supports
contain,cover, andfillmodes for flexible video scaling. - Optimized render size - Canvas render size is optimized to be as small as possible based on video dimensions and container size.
- Optimized GL context - Efficient WebGL context creation and cleanup.
import { TransparentVideo, TransparentVideoSource } from 'transparent-video-react';
<TransparentVideo autoPlay loop muted>
<TransparentVideoSource src="video.mp4" type="video/mp4" />
{/* or */}
{/* <TransparentVideo.Source src="video.mp4" type="video/mp4" /> */}
</TransparentVideo>;| Name | Type | Default | Description |
|---|---|---|---|
className |
string |
CSS class name for the container | |
style |
CSSProperties |
Inline CSS styles for the container | |
muted |
boolean |
Whether the video is muted | |
autoPlay |
boolean |
Whether the video plays automatically | |
loop |
boolean |
Whether the video loops | |
playsInline |
boolean |
Whether the video plays inline (mobile) | |
preMultipliedAlpha |
boolean |
false |
Whether the video uses premultiplied alpha |
objectFit |
'contain' | 'cover' | 'fill' |
'fill' |
How the video is fitted to the container |
videoProps |
React.HTMLProps<HTMLVideoElement> |
Additional props for the video element | |
canvasProps |
React.HTMLProps<HTMLCanvasElement> |
Additional props for the canvas element | |
children |
TransparentVideoSource | TransparentVideoSource[] |
<TransparentVideoSource> or <TransparentVideo.Source> nodes |
|
onPlayStateChange |
(isPlaying: boolean) => void |
Callback when play state changes |
| Name | Type | Default | Description |
|---|---|---|---|
src |
string |
URL of the video source | |
type |
string |
MIME type of the video (e.g., video/mp4) |
The TransparentVideo component exposes a TransparentVideoRef with the following methods and properties:
| Name | Type | Description |
|---|---|---|
play() |
() => void |
Play the video |
pause() |
() => void |
Pause the video |
seek(time) |
(time: number) => void |
Seek to a specific time (in seconds) |
forceUpdate(resize) |
(resize?: boolean) => void |
Force draw the current frame. When resize is true, the canvas layout is recalculated first. |
isPlaying |
boolean |
Whether the video is currently playing |
getVideoElement() |
() => HTMLVideoElement | null |
Get the underlying video element |
getCanvasElement() |
() => HTMLCanvasElement | null |
Get the underlying canvas element |
Install ffmpeg to generate a stacked alpha video from a video with alpha channel.
See Jake Archibald's repo for more details, here is the generation method taken from that doc:
This is the ideal format, used by Chrome, Firefox, and Safari on iPhone 15 Pro or M3 MacBook Pro & newer.
INPUT="in.mov" OUTPUT="av1.mp4" CRF=45 CPU=3 bash -c 'ffmpeg -y -i "$INPUT" -filter_complex "[0:v]format=pix_fmts=yuva444p[main]; [main]split[main][alpha]; [alpha]alphaextract[alpha]; [main][alpha]vstack" -pix_fmt yuv420p -an -c:v libaom-av1 -cpu-used "$CPU" -crf "$CRF" -pass 1 -f null /dev/null && ffmpeg -y -i "$INPUT" -filter_complex "[0:v]format=pix_fmts=yuva444p[main]; [main]split[main][alpha]; [alpha]alphaextract[alpha]; [main][alpha]vstack" -pix_fmt yuv420p -an -c:v libaom-av1 -cpu-used "$CPU" -crf "$CRF" -pass 2 -movflags +faststart "$OUTPUT"'CRF(0-63): Lower values are higher quality, larger filesize.CPU(0-8): Weirdly, lower values use more CPU, which improves quality, but encodes much slower. Usually not got lower than 3.
For Safari on other devices, they need a less-efficient HEVC.
INPUT="in.mov" OUTPUT="hevc.mp4" CRF=30 PRESET="veryslow" bash -c 'ffmpeg -y -i "$INPUT" -filter_complex "[0:v]format=pix_fmts=yuva444p[main]; [main]split[main][alpha]; [alpha]alphaextract[alpha]; [main][alpha]vstack" -pix_fmt yuv420p -an -c:v libx265 -preset "$PRESET" -crf "$CRF" -tag:v hvc1 -movflags +faststart "$OUTPUT"'CRF(0-63): Lower values are higher quality, larger filesize.PRESET(medium,slow,slower,veryslow): The slower you go, the better the output.
Features
- Support
object-position - Anti-aliasing
- Video poster
- Accessibility
Known issues
- First frame render sometimes is blank, find a better way to handle this
- If
autoPlayis enabled, since resize canvas is debounced (100ms), first 100ms video render will be skipped
- Jake Archibald for the original research and code
MIT