🌈 WebVideoCreator (abbreviated as WVC) is a framework for rendering web animations into videos. It's implemented based on Node.js + Puppeteer + Chrome + FFmpeg. It performs deterministic rendering and captures any HTML5-playable animations (CSS3 animations/SVG animations/Lottie animations/GIF animations/APNG animations/WEBP animations) and any timeline-based animations driven by RAF. You can also mischievously use setInterval
or setTimeout
to control animations. WVC supports embedding or exporting MP4 and transparent WebM videos, as well as features such as transition compositing, audio synthesis, and font loading. Let's get started with the Quick Start 🍻.
WVC creates a virtual time environment🕒 for your cool animated pages, which can be imagined as something akin to "The Truman Show." Its main responsibility is to transform an uncertain rendering environment into a deterministic rendering environment.
All of this is made possible by Chrome's provided deterministic rendering mode and support for the headless experimental API: HeadlessExperimental.beginFrame, This is an innovative experimental function 🧪.
Q&A and discussion group on QQ🐧: 752693580
- Developed using Node.js, it's very easy to use, extend, and develop further.
- Video processing is incredibly fast, rendering a video as long as 5 minutes can be completed in just 1 minute, Check Performance Tips for optimal performance.
- Supports rendering and compositing of single scenes and multi-scene videos, with the ability to apply transition effects to multi-scene videos.
- Supports chunked video compositing, allowing chunks to be distributed to multiple devices for rendering, then combined into multi-scene videos, significantly reducing rendering time for long videos.
- Supports parallel rendering and compositing of multiple video tasks.
- Support embedding or exporting webm format videos that support transparent channels.
- API support for distributed rendering, enabling the distribution of a large number of videos to multiple devices for rendering and final merging output with minimal wrapping of WVC.
- Supports GPU acceleration for rendering and compositing, reducing video rendering time significantly.
- Can be deployed and run on both Windows and Linux platforms, On MacOS, it is necessary to enable Compatible Rendering Mode.
WVC achieves perfect frame-by-frame capture of any animation on a web page. It minimizes the cost of achieving a WYSIWYG (What You See Is What You Get) experience. As a rendering backend, it currently explores the following applications:
📊 Data Visualization Video Rendering: Combine chart libraries such as ECharts to create animated charts on the web and capture them as videos using WVC. This is useful for creating dynamic ranking videos commonly seen on platforms like TikTok, especially when combined with web scraping for data collection.
👩🏫 Digital Human Video Rendering: With the popularity of AI-generated characters, various digital avatars appear on screens. WVC supports the use of transparent channel videos or mask videos on web pages, enhancing visual effects by incorporating digital humans into animations.
🎨 Content Creation Video Rendering: Design a simple frontend animation editor and previewer to meet content creation needs. Use WVC as the backend to achieve a WYSIWYG video effect.
🎮️ Game or User Operation Replay Video Rendering: Integrate with web-based games or applications to capture replays as videos, providing users with easy sharing and secondary editing options.
There are more application scenarios waiting for your exploration, and if you have great ideas, remember to raise an issue 🙋♀️...
💯 Perfect Capture: The browser's frame composer defaults to a throttling strategy to reduce resource consumption. When drawing complex graphics or under increased system load, web animations may experience frame skipping, dropping frames, or delays. Using screen recording tools makes it difficult to ensure that every frame is captured correctly. In contrast, WVC takes control of the timing, determining when to draw the next frame onto the screen.
🎞️ Parallel Rendering: Screen recording tools typically cannot capture the animation content of multiple tab pages simultaneously. However, WVC can capture animations in parallel across multiple pages and ultimately combine these chunks into a single long video, with support for seamless transitions.
🦾 Automation-Friendly: Screen recording tools require manual operation. With WVC, a set of web animation templates can be combined with data scrapers and scheduled tasks to automate video production.
🧩 Quick Integration: Screen recording tools are challenging to integrate. WVC, developed as an NPM package based on Node.js, can be quickly integrated on the backend. Some developers use it to capture game replays as videos.
We're also in need of animators! If you're passionate about open source projects, feel free to join us 😆.
Check out all the demos here: Rendering Example Page 🤗
In theory, all web animation/graphics libraries should work smoothly in the WVC environment. Below, I've listed only the libraries that I have verified to be compatible:
Anime.js / GSAP / D3.js / Three.js / Echart / Lottie-Web / PixiJS / Animate.css / Mo.js / Tween.js
Please note that if you manually use RAF to drive animations, ensure that you receive the timestamp
parameter from the callback to set the animation's progress to that timestamp. Otherwise, frame rate asynchrony may occur.
# Install WebVideoCreator from NPM
npm i web-video-creator
If you encounter issues with the download of ffmpeg-static
, please set the environment variable: FFMPEG_BINARIES_URL=https://cdn.npmmirror.com/binaries/ffmpeg-static
.
WVC needs to capture animations from web pages, and you can create a temporary web server locally to serve static pages for testing purposes. One of the simplest ways to do this is by using live-server
. If you already have static web pages, you can skip this step.
# Install live-server globally from NPM
npm i -g live-server
# Start the web server
live-server
Create a test page in the root directory of the web server. The following HTML content demonstrates an automatically rotating red triangle SVG animation:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test Page</title>
</head>
<body>
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon points="60,30 90,90 30,90" fill="red">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 60 70" to="360 60 70"
dur="10s" repeatCount="indefinite" />
</polygon>
</svg>
</body>
</html>
import WebVideoCreator, { VIDEO_ENCODER, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// Configure WVC
wvc.config({
// Choose an appropriate encoder based on your hardware. Here, we are using the h264_nvenc encoder for Nvidia graphics cards.
// Encoder selection can refer to docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// Create a single-scene video
const video = wvc.createSingleVideo({
// URL of the page to be rendered
url: "http://localhost:8080/test.html",
// or can directly set the page content:
// content: "<h1>Hello WebVideoCreator</h1>",
// Video width
width: 1280,
// Video height
height: 720,
// Video frame rate
fps: 30,
// Video duration
duration: 10000,
// Output path for the video
outputPath: "./test.mp4",
// Display progress bar in the command line
showProgress: true
});
// Listen for the completion event
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// Start rendering
video.start();
import WebVideoCreator, { VIDEO_ENCODER, TRANSITION, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// Configure WVC
wvc.config({
// Choose an appropriate encoder based on your hardware. Here, we are using the h264_nvenc encoder for Nvidia graphics cards.
// Encoder selection can refer to docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// Create a multi-scene video
const video = wvc.createMultiVideo({
// Video width
width: 1280,
// Video height
height: 720,
// Video frame rate
fps: 30,
// Video segment parameters
chunks: [
{
url: "http://localhost:8080/scene-1.html",
// or can directly set the page content:
// content: "<h1>Hello WebVideoCreator</h1>",
duration: 10000,
// Insert a circular crop transition between the first and second scenes
transition: TRANSITION.CIRCLE_CROP
},
{
url: "http://localhost:8080/scene-2.html",
duration: 10000
}
],
// Output path for the video
outputPath: "./test.mp4",
// Display progress bar in the command line
showProgress: true
});
// Listen for the completion event
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// Start rendering
video.start();
import WebVideoCreator, { VIDEO_ENCODER, TRANSITION, logger } from "web-video-creator";
const wvc = new WebVideoCreator();
// Configure WVC
wvc.config({
// Choose an appropriate encoder based on your hardware. Here, we are using the h264_nvenc encoder for Nvidia graphics cards.
// Encoder selection can refer to docs/video-encoder.md
mp4Encoder: VIDEO_ENCODER.NVIDIA.H264
});
// Create chunk video 1
const chunk1 = wvc.createChunkVideo({
url: "http://localhost:8080/scene-1.html",
// or can directly set the page content:
// content: "<h1>Hello WebVideoCreator</h1>",
width: 1280,
height: 720,
fps: 30,
duration: 10000,
showProgress: true
});
// Create chunk video 2
const chunk2 = wvc.createChunkVideo({
url: "http://localhost:8080/scene-2.html",
width: 1280,
height: 720,
fps: 30,
duration: 10000,
showProgress: true
});
// Wait for the chunks to finish rendering
await Promise.all([chunk1.startAndWait(), chunk2.startAndWait()]);
// Set the transition effect between chunk1 and chunk2 to fade in and out
chunk1.setTransition({ id: TRANSITION.FADE, duration: 500 });
// Create a multi-scene video
const video = wvc.createMultiVideo({
width: 1280,
height: 720,
fps: 30,
// Video segments
chunks: [
chunk1,
chunk2
],
// Output path for the video
outputPath: "./test.mp4",
// Display progress bar in the command line
showProgress: true
});
// Listen for the completion event
video.once("completed", result => {
logger.success(`Render Completed!!!\nvideo duration: ${Math.floor(result.duration / 1000)}s\ntakes: ${Math.floor(result.takes / 1000)}s\nRTF: ${result.rtf}`)
});
// Start rendering
video.start();
You can globally configure WVC to adjust some common parameters.
import WebVideoCreator, { VIDEO_ENCODER, AUDIO_ENCODER } from "web-video-creator";
const wvc = new WebVideoCreator();
wvc.config({
// Enable this to output some debug logs from WVC
debug: true,
// Specify the Chrome browser version to use
browserVersion: "...",
// It is recommended to enable this. If disabled, the window will be displayed and requires compatibility rendering mode to render properly. It's only used for debugging visuals.
browserHeadless: true,
// Frame rate limit is enabled by default. Disabling it can improve rendering efficiency and support animations above 60fps. However, this might disable GPU vertical sync, potentially causing screen tearing or other issues.
browserFrameRateLimit: true,
// Enable this to output browser runtime logs
browserDebug: true,
// Enable this to output each executed FFmpeg command
ffmpegDebug: true,
// Path to the FFmpeg executable file; setting this will disable the internal ffmpeg-static; it's recommended to use the internal FFmpeg for completeness
ffmpegExecutablePath: "...",
// Path to the ffprobe executable file; setting this will disable the internal ffprobe-static; it's recommended to use the internal ffprobe for completeness
ffprobeExecutablePath: "...",
// Browser GPU acceleration switch; recommended to enable for improved rendering performance; if you don't have a GPU or encounter rendering issues, you can disable it
browserUseGPU: true,
// Path to the browser executable file; setting this will disable the internal browser; it's recommended to use the internal browser for completeness
browserExecutablePath: "...",
// Allow unsafe contexts, false by default. Once enabled, users can navigate to unsafe URLs, but due to unsafe context restrictions, dynamic images and embedded videos cannot be used on the page
allowUnsafeContext: false,
// Compatible Rendering Mode is necessary on MacOS but not recommended for other environments; enabling this will disable HeadlessExperimental.beginFrame API calls and use regular Page.screenshot, which can lead to decreased rendering performance and frame rate desynchronization in some animations; you can try enabling it if you encounter the error "TargetCloseError: Protocol error (HeadlessExperimental.beginFrame): Target closed"
compatibleRenderingMode: false,
// Minimum number of browser instances in the resource pool
numBrowserMin: 1,
// Maximum number of browser instances in the resource pool
numBrowserMax: 5,
// Minimum number of page instances per browser instance
numPageMin: 1,
// Maximum number of page instances per browser instance
numPageMax: 5,
// User-agent string to use when accessing pages
userAgent: null,
// Frame capture quality (0-100); only effective for jpeg
frameQuality: 80,
// Frame image format (jpeg/png); it's recommended to use jpeg as png capture is more time-consuming
frameFormat: "jpeg",
// Timeout for capturing images with BeginFrame
beginFrameTimeout: 5000,
// Video encoder for MP4 format; by default, it uses the libx264 software encoder; it's recommended to choose the appropriate hardware encoder for acceleration based on your hardware, Encoder selection can refer to docs/video-encoder.md.
mp4Encoder: VIDEO_ENCODER.CPU.H264,
// Video encoder for WEBM format; by default, it uses the libvpx software encoder; it's recommended to choose the appropriate hardware encoder for acceleration based on your hardware
webmEncoder: VIDEO_ENCODER.CPU.VP8,
// Audio encoder; it's recommended to use the default AAC encoder
audioEncoder: AUDIO_ENCODER.AAC
});
To add audio to your rendered HTML, simply include an <audio>
element with the desired audio file. You can also set attributes like loop
, and WVC will automatically include the audio track for looping in the video.
<audio src="bgm.mp3" loop></audio>
You can also set various attributes to control the audio's behavior. These attributes do not always need to be paired, so you can customize them according to your needs.
<!-- Control the audio volume to half the original level -->
<audio src="bgm.mp3" volume="0.5"></audio>
<!-- Start playing the audio after 3 seconds and stop it at 10 seconds -->
<audio src="bgm.mp3" startTime="3000" endTime="10000"></audio>
<!-- Loop a segment of the audio from the 5th second to the 15th second -->
<audio src="bgm.mp3" seekStart="5000" seekEnd="15000" loop></audio>
<!-- Apply a 300ms fade-in and 500ms fade-out to the audio -->
<audio src="bgm.mp3" fadeInDuration="300" fadeOutDuration="500"></audio>
You can also dynamically add and remove <audio>
elements in your code to control audio entering and exiting the scene. WVC will detect them.
const audio = document.createElement("audio");
audio.src = "bgm.mp3";
// Audio enters the scene at 3 seconds
setTimeout(() => document.body.appendChild(audio), 3000);
// Audio exits the scene at 8 seconds
setTimeout(() => audio.remove(), 8000);
Or call captureCtx.addAudio on the page to add audio to the video.
// Add a single audio track
captureCtx.addAudio({
url: "bgm.mp3",
startTime: 500,
loop: true,
// 80% volume
volume: 80
});
// Add multiple audio tracks
captureCtx.addAudios([...]);
You can also directly use addAudio in WVC to add local or remote audio to the video.
const video = wvc.createSingleVideo({ ... });
// Add a single audio track
video.addAudio({
// url: "http://.../bgm.mp3"
path: "bgm.mp3",
startTime: 500,
loop: true,
// 80% volume
volume: 80
});
// Add multiple audio tracks
video.addAudios([...]);
This operation also applies to MultiVideo and ChunkVideo.
Currently, WVC supports mp4
and webm
video formats. To insert a video into your rendered HTML, include a <video>
element with the desired video file. You can set attributes like loop
and muted
. If your src does not contain the . mp4
suffix name, it may not be recognized. Please add the capture
attribute to identify the element that needs to be captured.
<video src="background.mp4" loop muted></video
If you wish to insert a video with a transparent channel, see: Transparent Channel Videos. If you are interested in video frame rate synchronization or transparent video rendering, you can refer to: Technical Implementation.
Similar to audio, you can set various attributes to control the video's behavior, and these attributes do not always need to be paired.
<!-- Control the audio volume to 70% of the original -->
<video src="test.mp4" volume="0.7"></video>
<!-- Start playing the video after 3 seconds and stop it at 10 seconds -->
<video src="test.mp4" startTime="3000" endTime="10000"></video>
<!-- Loop a segment of the video from the 5th second to the 15th second -->
<video src="test.mp4" seekStart="5000" seekEnd="15000" loop></video>
<!-- Apply a 300ms fade-in and 500ms fade-out to the video -->
<video src="test.mp4" fadeInDuration="300" fadeOutDuration="500"></video>
You can dynamically add and remove <video>
elements in your code to control video entering and exiting the scene. WVC will detect them.
const video = document.createElement("video");
video.src = "test.mp4";
// Video enters the scene at 3 seconds
setTimeout(() => document.body.appendChild(video), 3000);
// Video exits the scene at 8 seconds
setTimeout(() => video.remove(), 8000);
If you are using some front-end frameworks to implement animation content, WVC may not be able to listen to your changes to the <video>
element (such as hiding or displaying). Please replace the element with the <canvas video-capture>
element and prompt WVC to notice that it is a video canvas through video-capture
attribute.
<canvas src="test.mp4" video-capture></canvas>
Transparent videos are great for compositing digital avatars (e.g., VTubers) into video scenes. Please refer to Rendering Example Page Last Demo
In WVC, transparent videos should be in the webm
format. Internally, they will be re-encoded into two mp4 container videos: one for the color base video and one for the mask video. These videos will be used for blending and drawing using the globalCompositeOperation
in the browser canvas.
For users, it's seamless. You just need to include a <video>
element in your HTML with the src
set to the webm video file.
<video src="vtuber.webm"></video>
Webm encoding and decoding can be time-consuming. If you can obtain the original mp4 video and the mask mp4 video, it's a better solution. Just add the maskSrc
attribute.
<video src="vtuber.mp4" maskSrc="vtuber_mask.mp4"></video>
Animated images refer to sequence frame animations in gif
/ apng
/ webp
formats. They can naturally play in the browser, but their frame rate is usually uncontrollable. WVC proxies their rendering, replacing img
elements with canvas
elements, and uses ImageDecoder to decode and draw each frame in sync with virtual time.
The following animated images can be rendered as well, and you can style them as usual.
<img src="test.gif"/>
<img src="test.apng"/>
<img src="test.webp"/>
If you are using some front-end frameworks to implement animation content, WVC may not be able to listen to your changes to the <img>
element (such as hiding or displaying). Please replace the element with the <canvas dyimage-capture>
element and prompt WVC to notice that it is a dyanmic image canvas through dyimage-capture
attribute.
<canvas src="test.gif" dyimage-capture></canvas>
WVC comes with the built-in lottie-web animation library. If you have your own Lottie animations in your web page, they should work seamlessly with WVC.
Simply insert a <lottie>
element and set the src
attribute.
<lottie src="example.json"></lottie>
If you are using some front-end frameworks to implement animation content, WVC may not be able to listen to your changes to the <lottie>
element (such as hiding or displaying). Please replace the element with the <canvas lottie-capture>
element and prompt WVC to notice that it is a lottie canvas through lottie-capture
attribute.
<canvas src="example.json" lottie-capture></canvas>
WVC can detect @font-face
declarations in stylesheets and wait for the fonts to load before starting rendering.
<style>
@font-face {
font-family: "FontTest";
src: url("font.ttf") format("truetype");
}
</style>
<p style='font-family: "FontTest"'>Hello World</p>
Alternatively, you can register local or remote fonts through code.
const video = wvc.createSingleVideo({ ... });
// Register a single font
video.registerFont({
// url: "http://.../font.ttf"
path: "font.ttf",
family: "FontTest",
format: "truetype"
});
// Register multiple fonts
video.registerFonts([...]);
Make sure the fonts can be loaded; otherwise, rendering may not start.
WVC supports the use of Xfade filters supported by FFmpeg to create transition effects. You can refer to the list of transitions.
Each chunk video parameter can be configured with a transition effect and its duration.
import WebVideoCreator, { TRANSITION } from "web-video-creator";
...
const video = wvc.createMultiVideo({
...
// Video segment parameters
chunks: [
{
url: "http://localhost:8080/scene-1.html",
duration: 10000,
// Insert a fade in/out transition between the first and second scenes
transition: {
id: TRANSITION.FADE,
duration: 500
},
// If you don't need to set the duration, you can directly set the transition ID
// transition: TRANSITION.FADE
},
{
url: "http://localhost:8080/scene-2.html",
duration: 10000
}
],
...
});
...
It's important to note that applying transitions will result in a shorter total video duration since the transition effect effectively overlaps a portion of two video segments. For example, if you insert a fade transition between two 5-second segments, the resulting video will have a duration of 9 seconds.
Lottie animations are also suitable for use as transition effects. You can play a full-screen Lottie animation for half of the duration at the end of one video segment and then play another full-screen Lottie animation for the remaining half at the beginning of the next video segment to create more dynamic transition effects.
WVC supports you to set the background opacity backgroundOpacity
option to achieve transparent or semi transparent background video output, with a value range of 0-1, Please ensure that the output video path suffix name or format option is webm.
const video = wvc.createSingleVideo({
...,
// Set a completely transparent background
backgroundOpacity: 0
});
By default, WVC starts rendering immediately after the page navigation is complete. If you
want to perform some tasks before rendering, you can disable automatic rendering start in the options. In this case, remember to call captureCtx.start() in your page code, or rendering will be blocked indefinitely.
const video = wvc.createSingleVideo({
url: "http://localhost:8080/test.html",
width: 1280,
height: 720,
duration: 10000,
// Disable automatic rendering start
autostartRender: false
});
In your page code, call the start function when appropriate.
<script>
// 数据加载完成后启动渲染
loadData()
.then(() => captureCtx.start())
.catch(err => console.error(err));
</script>
By default, WVC begins capturing the screen from the 0 time point after rendering starts. However, it also supports initiating screen capture from other time points.
const video = wvc.createSingleVideo({
url: "http://localhost:8080/test.html",
width: 1280,
height: 720,
// Start capturing from the 5-second mark
startTime: 5000,
duration: 10000
});
const video = wvc.createSingleVideo({
url: "http://localhost:8080/test.html",
width: 1280,
height: 720,
duration: 10000,
pagePrepareFn: async page => {
// Get the puppeteer Page object
const _page = page.target;
// Click the button
await _page.tap("#play-button");
}
});
WVC allows you to set actions to be executed at specific time points within a video. This feature enables convenient manipulation of a page at arbitrary intervals within a video. The following code is used to perform scrolling at the 3rd, 6th, and 9th seconds of the video.
const actionFn = async (page) => {
const _page = page.target;
await _page.evaluate(() => {
window.scrollTo({
top: window.scrollY + 1280,
behavior: "smooth"
});
});
};
const video = wvc.createSingleVideo({
width: 1080,
height: 1280,
fps: 60,
outputPath: "./t2.mp4",
showProgress: true,
url: "https://www.bilibili.com/v/popular/all/",
// Setting up the action sequence
timeActions: {
3000: actionFn,
6000: actionFn,
9000: actionFn
},
duration: 10000
});
If you want to see the page's logs, you can enable consoleLog
in the video options. If there are embedded videos, you can also enable videoPreprocessLog
to output video preprocessing logs.
const video = wvc.createSingleVideo({
...,
// Output logs from the page's console
consoleLog: true,
// Output video preprocessing logs for embedded videos
videoPreprocessLog: true
});
After rendering a video, you can capture a frame image and save it, which can be used as the video's cover image.
const video = wvc.createSingleVideo({
...,
// Enable cover image capture
coverCapture: true,
// Time in milliseconds to capture the image (default is 20% of the video's duration)
coverCaptureTime: 1000,
// Image format for capture (jpg/png/bmp), default is jpg
coverCaptureFormat: "jpg"
});
WVC supports inserting an image into the first frame of the video, which will be displayed when the video is not playing.
const video = wvc.createSingleVideo({
...,
// Set the path to the additional cover image, supports jpg/png/bmp
attachCoverPath: "./cover.jpg"
});
You can control the overall volume of the output video.
const video = wvc.createSingleVideo({
...,
// Set the video volume to 80% of the original
volume: 80
});
WVC allows you to control video image quality using videoQuality
or videoBitrate
.
videoQuality
is used to calculate the bitrate based on the total pixel count of the image. The following is the method used by WVC to calculate the video bitrate internally:
const pixels = width * height;
const videoBitrate = (2560 / 921600 * pixels) * (videoQuality / 100);
You can provide videoQuality
(0-100) in the video options:
const video = wvc.createSingleVideo({
...,
// Set video quality to 80%
videoQuality: 80
});
If you find the bitrate inappropriate, you can set videoBitrate
separately:
const video = wvc.createSingleVideo({
...,
// Set video bitrate to 8Mbps
videoBitrate: "8192k"
});
Additionally, you can adjust the frame image quality when using jpeg as the frame format by setting frameQuality
. For details, see Global Configuration.
You can also adjust audio quality by setting the audio bitrate audioBitrate
:
const video = wvc.createSingleVideo({
...,
// Set audio bitrate to 320Kbps
audioBitrate: "320k"
});
WVC currently supports output in yuv420p
, yuv444p
, and rgb24
pixel formats. It defaults to using the more compatible yuv420p
. If you notice significant color differences between the output video and the original page, you can switch to rgb24
to improve the issue.
const video = wvc.createSingleVideo({
...,
// Set the pixel format to rgb24
pixelFormat: "rgb24"
});
When rendering frames from the browser and feeding them into FFmpeg, it's necessary to encode the image data at the specified frame rate and store it in a specified format container using a video encoder. Video encoding is a resource-intensive operation, and choosing a hardware encoder can accelerate this process and reduce CPU load.
Please refer to the Video Encoder Guide for a list of supported video encoders by WVC.
You can use the progress
event of the video instance to monitor the rendering and synthesis progress.
const video = wvc.createSingleVideo({ ... });
video.on("progress", (progress, synthesizedFrameCount, totalFrameCount) => {
// Output progress / synthesized frame count / total frame count
console.log(progress, synthesizedFrameCount, totalFrameCount);
});
This also applies to MultiVideo
/ ChunkVideo
and the synthesisers in the low-level API.
You can actively throw errors in the page to interrupt rendering.
<script>
captureCtx.throwError(-1, "Abort");
</script>
If your page involves heavy computations or consumes excessive memory, it may crash, causing rendering to be interrupted.
If you're using the high-level API, you can be notified of page crashes through the pageCrashed
event of the video instance.
const video = wvc.createSingleVideo({ ... });
// Output crash errors when an error occurs
video.on("pageCrashed", err => console.error(err));
When using the low-level API, you can listen for page crashes through the crashed
event of the Page instance.
// Output crash errors when an error occurs
page.on("crashed", err => console.error(err));
If you're using the high-level API, you can listen for errors through the error
event of the video instance.
const video = wvc.createSingleVideo({ ... });
video.on("error", err => console.error(err));
When using the low-level API, you can listen for errors through the error
event of the Page instance.
page.on("error", err => console.error(err));
On MacOS, due to Chrome's lack of support for the BeginFrame API, it is necessary to switch to the compatible rendering mode for proper functionality. This mode may result in a decrease in rendering efficiency of around 40%. It is recommended to deploy on Windows or Linux devices for better performance.
// Enable compatible rendering mode
wvc.config({ compatibleRenderingMode: true });
To optimize task launching and rendering time, WVC generally maintains four types of caches: Browser Cache
, Preprocess Cache
, Synthesize Cache
, and Local Font Cache
.
Browser Cache: Contains cached files and records generated by the page, reducing page loading time.
Preprocess Cache: When rendering content references remote resources, the preprocessor attempts to fetch and cache them locally, reducing bandwidth consumption.
Synthesize Cache: When combining multiple video chunks into a whole, pre-rendered chunks serve as the synthesize cache. Typically, WVC clears this cache after synthesis is complete.
Local Font Cache: When using registerFonts
to register local fonts for successful injection into the page, a copy of fonts from the source path is stored in the cache.
// Clear browser cache
wvc.cleanBrowserCache();
// Clear preprocess cache
wvc.cleanPreprocessCache();
// Clear synthesize cache
wvc.cleanSynthesizeCache();
// Clear local font cache
wvc.cleanLocalFontCache();
In most cases, it is recommended to use the high-level API because it is simple but may be less flexible.
If you have multiple devices available for rendering, you can deploy WVC on these devices. WVC provides MultiVideo
and ChunkVideo
, allowing you to divide the animation pages into many segments (e.g., 0-10 seconds, 10-20 seconds, etc.). Distribute their parameters to different WVC instances on different devices, create ChunkVideo instances on these devices, and execute parallel rendering to generate multiple video segments (ts
). These segments are then sent back to the core node, where they are combined, and transitions, audio tracks, and output are handled. The distribution and return process is not yet implemented in WVC, but it is not difficult, and you can wrap it according to your own scenario. Contributions to WVC are welcome through PR!
Performance is typically influenced by the complexity of animations and media. To improve performance:
-
Divide long animations into multiple segments. For instance, you can include a seek parameter with each page URL, load the page, and seek to a specified time to start playing. Then, render and combine them as a multi-video to significantly reduce rendering time for long videos.
-
Render more video chunks in parallel. To maximize system resources, select the number of parallel chunks based on the number of CPU threads, assuming your system has sufficient memory.
-
CPU clock speed has a significant impact on baseline performance. Consumer-grade CPUs often have high clock speeds, which can lead to better performance.
-
Consider using GPU acceleration for rendering and compositing. If your device has a GPU but it's not being utilized, check the configuration settings or report the issue.
-
Using an SSD (Solid State Drive) can improve hard disk cache write performance during parallel rendering, reducing rendering time.
-
Select the right video hardware encoder. By default, software encoders are used (libx264 for mp4 and libvpx for webm). If you have integrated or discrete graphics, configure the hardware encoders they support.
-
Some time may be spent on network file transfers. It's advisable to deploy static file services on the same server or access the file server from a local network.
-
Reducing the output video resolution and frame rate is the most effective way to reduce time consumption.
Here are the performance parameters for my personal computer as a reference:
Operating System: Windows 10 (Better performance on Linux)
CPU: AMD Ryzen 7 3700X (Clock speed 3.6-4.4GHz, 8 cores, 16 threads)
GPU: Nvidia GeForce GTX 1660 SUPER (6GB VRAM, NVENC support)
RAM: 16GB (DDR4 2400MHz)
Video Type: SVG animations + GIFs + Lottie animations
Video Resolution: 1280x720
Video Frame Rate: 30
Video Duration: 300s (5 minutes)
Rendering Time: 61s (1 minute)
Real-Time Rate: 4.844
Parallel Rendering Count: 16
-
Constrained by browser secure context restrictions, WebVideoCreator can only access
localhost
/127.0.0.1
or domains using HTTPS with valid certificates. For security reasons, it's recommended to use a local static server (e.g.,live-server
is a good choice), You can also use the content option to directly set the page content to avoid URL restrictions. -
The headless experimental API on Mac systems may cause crashes and needs to be switched to compatibility rendering mode to run. However, compatibility rendering mode has various issues, so it is not recommended for Mac systems. See Compatibility Rendering Mode.
-
WebVideoCreator is a pure ESM package and cannot be imported using CommonJS-style
require
. If you still want to userequire
to import it, refer to this gist for guidance. -
The page jump request during the video rendering process will be intercepted because jumping to the page will result in the loss of capture context.