פתחו סטרימינג וידאו באיכות גבוהה בדפדפן. למדו כיצד ליישם סינון טמפורלי מתקדם להפחתת רעש באמצעות WebCodecs API ומניפולציה של VideoFrame.
שליטה ב-WebCodecs: שיפור איכות וידאו עם הפחתת רעש טמפורלית
בעולם של תקשורת וידאו מבוססת-רשת, סטרימינג ויישומים בזמן אמת, האיכות היא מעל הכל. משתמשים ברחבי העולם מצפים לווידאו חד וברור, בין אם הם בפגישת עסקים, צופים באירוע חי או מקיימים אינטראקציה עם שירות מרוחק. עם זאת, זרמי וידאו סובלים לעיתים קרובות מארטיפקט עיקש ומסיח דעת: רעש. רעש דיגיטלי זה, שנראה לעיתים קרובות כמרקם מגורען או סטטי, עלול לפגוע בחוויית הצפייה, ובאופן מפתיע, להגדיל את צריכת רוחב הפס. למרבה המזל, API דפדפן רב-עוצמה, WebCodecs, מעניק למפתחים שליטה נמוכת-רמה חסרת תקדים כדי להתמודד עם בעיה זו באופן ישיר.
מדריך מקיף זה ייקח אתכם לצלילה עמוקה בשימוש ב-WebCodecs עבור טכניקת עיבוד וידאו ספציפית ובעלת השפעה גבוהה: הפחתת רעש טמפורלית. אנו נחקור מהו רעש וידאו, מדוע הוא מזיק, וכיצד תוכלו למנף את אובייקט VideoFrame
כדי לבנות צינור סינון ישירות בדפדפן. נכסה הכל מהתיאוריה הבסיסית ועד למימוש מעשי ב-JavaScript, שיקולי ביצועים עם WebAssembly, ומושגים מתקדמים להשגת תוצאות ברמה מקצועית.
מהו רעש וידאו ומדוע הוא חשוב?
לפני שנוכל לתקן בעיה, עלינו להבין אותה תחילה. בווידאו דיגיטלי, רעש מתייחס לווריאציות אקראיות בבהירות או במידע הצבע באות הווידאו. זוהי תוצר לוואי לא רצוי של תהליך לכידת התמונה והשידור.
מקורות וסוגי רעש
- רעש חיישן: האשם העיקרי. בתנאי תאורה נמוכה, חיישני מצלמה מגבירים את האות הנכנס כדי ליצור תמונה בהירה מספיק. תהליך הגברה זה מגביר גם תנודות אלקטרוניות אקראיות, מה שגורם לגרעיניות נראית לעין.
- רעש תרמי: חום הנוצר על ידי האלקטרוניקה של המצלמה יכול לגרום לאלקטרונים לנוע באופן אקראי, וליצור רעש שאינו תלוי ברמת האור.
- רעש קוונטיזציה: נוצר במהלך תהליכי ההמרה מאנלוגי לדיגיטלי והדחיסה, שבהם ערכים רציפים ממופים לקבוצה מוגבלת של רמות בדידות.
רעש זה מתבטא בדרך כלל כרעש גאוסיאני, שבו עוצמת כל פיקסל משתנה באופן אקראי סביב ערכו האמיתי, ויוצר גרעיניות עדינה ומרצדת על פני כל הפריים.
ההשפעה הכפולה של רעש
רעש וידאו הוא יותר מסתם בעיה קוסמטית; יש לו השלכות טכניות ותפיסתיות משמעותיות:
- חווית משתמש ירודה: ההשפעה הברורה ביותר היא על האיכות החזותית. וידאו רועש נראה לא מקצועי, מסיח את הדעת, ויכול להקשות על הבחנה בפרטים חשובים. ביישומים כמו שיחות ועידה, הוא יכול לגרום למשתתפים להיראות מגורענים ולא ברורים, מה שפוגע בתחושת הנוכחות.
- יעילות דחיסה מופחתת: זוהי הבעיה הפחות אינטואיטיבית אך קריטית לא פחות. מקודדי וידאו מודרניים (כמו H.264, VP9, AV1) משיגים יחסי דחיסה גבוהים על ידי ניצול יתירות. הם מחפשים קווי דמיון בין פריימים (יתירות טמפורלית) ובתוך פריים בודד (יתירות מרחבית). רעש, מטבעו, הוא אקראי ובלתי צפוי. הוא שובר את דפוסי היתירות הללו. המקודד רואה את הרעש האקראי כפרט בתדר גבוה שיש לשמר, מה שמאלץ אותו להקצות יותר ביטים לקידוד הרעש במקום התוכן הממשי. התוצאה היא קובץ גדול יותר עבור אותה איכות נתפסת, או איכות נמוכה יותר באותו קצב סיביות.
על ידי הסרת רעש לפני הקידוד, אנו יכולים להפוך את אות הווידאו לצפוי יותר, מה שמאפשר למקודד לעבוד ביעילות רבה יותר. זה מוביל לאיכות חזותית טובה יותר, שימוש נמוך יותר ברוחב פס וחווית סטרימינג חלקה יותר למשתמשים בכל מקום.
הכירו את WebCodecs: העוצמה של שליטה נמוכת-רמה בווידאו
במשך שנים, מניפולציית וידאו ישירה בדפדפן הייתה מוגבלת. מפתחים היו כלואים במידה רבה ביכולות של אלמנט ה-<video>
וה-Canvas API, שלעיתים קרובות היו כרוכים בקריאות חוזרות מה-GPU שפגעו בביצועים. WebCodecs משנה את כללי המשחק לחלוטין.
WebCodecs הוא API נמוך-רמה המספק גישה ישירה למקודדים ולמפענחים המובנים של הדפדפן. הוא מיועד ליישומים הדורשים שליטה מדויקת על עיבוד מדיה, כגון עורכי וידאו, פלטפורמות גיימינג בענן ולקוחות תקשורת מתקדמים בזמן אמת.
הרכיב המרכזי שנתמקד בו הוא אובייקט ה-VideoFrame
. VideoFrame
מייצג פריים בודד של וידאו כתמונה, אך הוא הרבה יותר ממפת סיביות פשוטה. זהו אובייקט יעיל ביותר, הניתן להעברה, שיכול להחזיק נתוני וידאו בפורמטים שונים של פיקסלים (כמו RGBA, I420, NV12) ונושא מטא-דאטה חשוב כמו:
timestamp
: זמן ההצגה של הפריים במיקרו-שניות.duration
: משך הזמן של הפריים במיקרו-שניות.codedWidth
ו-codedHeight
: מימדי הפריים בפיקסלים.format
: פורמט הפיקסלים של הנתונים (למשל, 'I420', 'RGBA').
באופן מכריע, VideoFrame
מספק מתודה בשם copyTo()
, המאפשרת לנו להעתיק את נתוני הפיקסלים הגולמיים והלא-דחוסים לתוך ArrayBuffer
. זוהי נקודת הכניסה שלנו לניתוח ומניפולציה. ברגע שיש לנו את הבתים הגולמיים, אנו יכולים להחיל את אלגוריתם הפחתת הרעש שלנו ולאחר מכן לבנות VideoFrame
חדש מהנתונים שעברו שינוי כדי להעביר הלאה בצינור העיבוד (למשל, למקודד וידאו או לקנבס).
הבנת סינון טמפורלי
ניתן לחלק באופן כללי את טכניקות הפחתת הרעש לשני סוגים: מרחבי וטמפורלי.
- סינון מרחבי: טכניקה זו פועלת על פריים בודד בבידוד. היא מנתחת את היחסים בין פיקסלים שכנים כדי לזהות ולהחליק רעש. דוגמה פשוטה היא מסנן טשטוש (blur). למרות יעילותם בהפחתת רעש, מסננים מרחביים יכולים גם לרכך פרטים וקצוות חשובים, מה שמוביל לתמונה פחות חדה.
- סינון טמפורלי: זוהי הגישה המתוחכמת יותר שבה אנו מתמקדים. היא פועלת על פני מספר פריימים לאורך זמן. העיקרון הבסיסי הוא שתוכן הסצנה הממשי צפוי להיות מתואם מפריים אחד למשנהו, בעוד שהרעש הוא אקראי וחסר קורלציה. על ידי השוואת ערך של פיקסל במיקום ספציפי על פני מספר פריימים, אנו יכולים להבחין בין האות העקבי (התמונה האמיתית) לבין התנודות האקראיות (הרעש).
הצורה הפשוטה ביותר של סינון טמפורלי היא מיצוע טמפורלי. דמיינו שיש לכם את הפריים הנוכחי ואת הפריים הקודם. עבור כל פיקסל נתון, הערך ה'אמיתי' שלו נמצא ככל הנראה איפשהו בין ערכו בפריים הנוכחי לערכו בפריים הקודם. על ידי מיזוגם, אנו יכולים למצע את הרעש האקראי. ניתן לחשב את ערך הפיקסל החדש באמצעות ממוצע משוקלל פשוט:
new_pixel = (alpha * current_pixel) + ((1 - alpha) * previous_pixel)
כאן, alpha
הוא מקדם מיזוג בין 0 ל-1. alpha
גבוה יותר אומר שאנו סומכים יותר על הפריים הנוכחי, מה שגורם להפחתת רעש פחותה אך לפחות ארטיפקטים של תנועה. alpha
נמוך יותר מספק הפחתת רעש חזקה יותר אך עלול לגרום ל'מריחות' או שובלים באזורים עם תנועה. מציאת האיזון הנכון היא המפתח.
מימוש מסנן מיצוע טמפורלי פשוט
בואו נבנה מימוש מעשי של קונספט זה באמצעות WebCodecs. צינור העיבוד שלנו יכלול שלושה שלבים עיקריים:
- קבלת זרם של אובייקטי
VideoFrame
(למשל, ממצלמת רשת). - עבור כל פריים, החלת המסנן הטמפורלי שלנו באמצעות נתוני הפריים הקודם.
- יצירת
VideoFrame
חדש ונקי.
שלב 1: הגדרת זרם הפריימים
הדרך הקלה ביותר לקבל זרם חי של אובייקטי VideoFrame
היא באמצעות MediaStreamTrackProcessor
, הצורך MediaStreamTrack
(כמו אחד מ-getUserMedia
) וחושף את הפריימים שלו כזרם קריא.
הגדרת קונספטואלית ב-JavaScript:
async function setupVideoStream() {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const track = stream.getVideoTracks()[0];
const trackProcessor = new MediaStreamTrackProcessor({ track });
const reader = trackProcessor.readable.getReader();
let previousFrameBuffer = null;
let previousFrameTimestamp = -1;
while (true) {
const { value: frame, done } = await reader.read();
if (done) break;
// כאן נעבד כל 'frame'
const processedFrame = await applyTemporalFilter(frame, previousFrameBuffer);
// לאיטרציה הבאה, נצטרך לאחסן את הנתונים של ה-frame המקורי הנוכחי
// יש להעתיק את נתוני הפריים המקורי אל 'previousFrameBuffer' כאן לפני סגירתו.
// אל תשכחו לסגור פריימים כדי לשחרר זיכרון!
frame.close();
// בצעו משהו עם ה-processedFrame (למשל, רינדור לקנבס, קידוד)
// ... ואז סגרו גם אותו!
processedFrame.close();
}
}
שלב 2: אלגוריתם הסינון - עבודה עם נתוני פיקסלים
זהו לב העבודה שלנו. בתוך פונקציית applyTemporalFilter
שלנו, אנו צריכים לגשת לנתוני הפיקסלים של הפריים הנכנס. לשם הפשטות, נניח שהפריימים שלנו הם בפורמט 'RGBA'. כל פיקסל מיוצג על ידי 4 בתים: אדום, ירוק, כחול ואלפא (שקיפות).
async function applyTemporalFilter(currentFrame, previousFrameBuffer) {
// הגדרת מקדם המיזוג שלנו. 0.8 אומר 80% מהפריים החדש ו-20% מהישן.
const alpha = 0.8;
// קבלת המימדים
const width = currentFrame.codedWidth;
const height = currentFrame.codedHeight;
// הקצאת ArrayBuffer שיחזיק את נתוני הפיקסלים של הפריים הנוכחי.
const currentFrameSize = width * height * 4; // 4 בתים לפיקסל עבור RGBA
const currentFrameBuffer = new Uint8Array(currentFrameSize);
await currentFrame.copyTo(currentFrameBuffer);
// אם זהו הפריים הראשון, אין פריים קודם למזג איתו.
// פשוט נחזיר אותו כפי שהוא, אך נאחסן את המאגר שלו לאיטרציה הבאה.
if (!previousFrameBuffer) {
const newFrameBuffer = new Uint8Array(currentFrameBuffer);
// נעדכן את ה-'previousFrameBuffer' הגלובלי שלנו עם המאגר הזה מחוץ לפונקציה.
return { buffer: newFrameBuffer, frame: currentFrame };
}
// יצירת מאגר חדש עבור פריים הפלט שלנו.
const outputFrameBuffer = new Uint8Array(currentFrameSize);
// לולאת העיבוד הראשית.
for (let i = 0; i < currentFrameSize; i++) {
const currentPixelValue = currentFrameBuffer[i];
const previousPixelValue = previousFrameBuffer[i];
// החלת נוסחת המיצוע הטמפורלי לכל ערוץ צבע.
// אנו מדלגים על ערוץ האלפא (כל בית רביעי).
if ((i + 1) % 4 !== 0) {
outputFrameBuffer[i] = Math.round(alpha * currentPixelValue + (1 - alpha) * previousPixelValue);
} else {
// שמירה על ערוץ האלפא כפי שהוא.
outputFrameBuffer[i] = currentPixelValue;
}
}
return { buffer: outputFrameBuffer, frame: currentFrame };
}
הערה על פורמטי YUV (I420, NV12): בעוד ש-RGBA קל להבנה, רוב הווידאו מעובד באופן טבעי במרחבי צבע YUV מטעמי יעילות. הטיפול ב-YUV מורכב יותר מכיוון שמידע הצבע (U, V) והבהירות (Y) מאוחסנים בנפרד (ב'מישורים'). לוגיקת הסינון נשארת זהה, אך תצטרכו לעבור על כל מישור (Y, U, ו-V) בנפרד, תוך שימת לב למימדים שלהם (מישורי הצבע הם לעיתים קרובות ברזולוציה נמוכה יותר, טכניקה הנקראת דגימת צבע מופחתת - chroma subsampling).
שלב 3: יצירת ה-VideoFrame
המסונן החדש
לאחר סיום הלולאה שלנו, outputFrameBuffer
מכיל את נתוני הפיקסלים עבור הפריים החדש והנקי יותר שלנו. כעת אנו צריכים לעטוף זאת באובייקט VideoFrame
חדש, ולוודא שאנו מעתיקים את המטא-דאטה מהפריים המקורי.
// בתוך הלולאה הראשית שלכם אחרי קריאה ל-applyTemporalFilter...
const { buffer: processedBuffer, frame: originalFrame } = await applyTemporalFilter(frame, previousFrameBuffer);
// יצירת VideoFrame חדש מהמאגר המעובד שלנו.
const newFrame = new VideoFrame(processedBuffer, {
format: 'RGBA',
codedWidth: originalFrame.codedWidth,
codedHeight: originalFrame.codedHeight,
timestamp: originalFrame.timestamp,
duration: originalFrame.duration
});
// חשוב: עדכון מאגר הפריים הקודם לאיטרציה הבאה.
// אנו צריכים להעתיק את נתוני הפריים ה*מקור*י, לא את הנתונים המסוננים.
// יש ליצור עותק נפרד לפני הסינון.
previousFrameBuffer = new Uint8Array(originalFrameData);
// כעת ניתן להשתמש ב-'newFrame'. רנדרו אותו, קדדו אותו, וכו'.
// renderer.draw(newFrame);
// ובאופן קריטי, סגרו אותו בסיום השימוש כדי למנוע דליפות זיכרון.
newFrame.close();
ניהול זיכרון הוא קריטי: אובייקטי VideoFrame
יכולים להחזיק כמויות גדולות של נתוני וידאו לא דחוסים ועשויים להיות מגובים בזיכרון מחוץ לערימת ה-JavaScript. עליכם חובה לקרוא ל-frame.close()
על כל פריים שסיימתם איתו. אי עשייה כן תוביל במהירות להתרוקנות הזיכרון ולקריסת הלשונית.
שיקולי ביצועים: JavaScript לעומת WebAssembly
המימוש ב-JavaScript טהור שהוצג לעיל מצוין ללימוד ולהדגמה. עם זאת, עבור וידאו של 30 פריימים לשנייה ברזולוציית 1080p (1920x1080), הלולאה שלנו צריכה לבצע מעל 248 מיליון חישובים בשנייה! (1920 * 1080 * 4 בתים * 30 פריימים לשנייה). בעוד שמנועי JavaScript מודרניים הם מהירים להפליא, עיבוד זה לכל פיקסל הוא מקרה שימוש מושלם לטכנולוגיה מוכוונת ביצועים יותר: WebAssembly (Wasm).
גישת ה-WebAssembly
WebAssembly מאפשר לכם להריץ קוד שנכתב בשפות כמו C++, Rust או Go בדפדפן במהירות כמעט-מקומית. הלוגיקה של המסנן הטמפורלי שלנו פשוטה למימוש בשפות אלו. הייתם כותבים פונקציה שמקבלת מצביעים למאגרי הקלט והפלט ומבצעת את אותה פעולת מיזוג איטרטיבית.
פונקציית C++ קונספטואלית עבור Wasm:
extern "C" {
void apply_temporal_filter(unsigned char* current_frame, unsigned char* previous_frame, unsigned char* output_frame, int buffer_size, float alpha) {
for (int i = 0; i < buffer_size; ++i) {
if ((i + 1) % 4 != 0) { // דלג על ערוץ אלפא
output_frame[i] = (unsigned char)(alpha * current_frame[i] + (1.0 - alpha) * previous_frame[i]);
} else {
output_frame[i] = current_frame[i];
}
}
}
}
מצד ה-JavaScript, הייתם טוענים את מודול ה-Wasm המהודר הזה. יתרון הביצועים המרכזי נובע משיתוף זיכרון. ניתן ליצור ArrayBuffer
s ב-JavaScript המגובים בזיכרון הליניארי של מודול ה-Wasm. זה מאפשר לכם להעביר את נתוני הפריים ל-Wasm ללא כל העתקה יקרה. כל לולאת עיבוד הפיקסלים רצה אז כקריאת פונקציית Wasm אחת, ממוטבת ביותר, שהיא מהירה משמעותית מלולאת `for` ב-JavaScript.
טכניקות סינון טמפורלי מתקדמות
מיצוע טמפורלי פשוט הוא נקודת פתיחה נהדרת, אך יש לו חיסרון משמעותי: הוא יוצר טשטוש תנועה או 'מריחות'. כאשר אובייקט נע, הפיקסלים שלו בפריים הנוכחי מתמזגים עם פיקסלי הרקע מהפריים הקודם, ויוצרים שובל. כדי לבנות מסנן ברמה מקצועית באמת, עלינו לקחת בחשבון את התנועה.
סינון טמפורלי עם פיצוי תנועה (MCTF)
תקן הזהב להפחתת רעש טמפורלית הוא סינון טמפורלי עם פיצוי תנועה. במקום למזג באופן עיוור פיקסל עם זה שבאותה קואורדינטה (x, y) בפריים הקודם, MCTF מנסה תחילה להבין מאיפה הפיקסל הזה הגיע.
התהליך כולל:
- הערכת תנועה: האלגוריתם מחלק את הפריים הנוכחי לבלוקים (למשל, 16x16 פיקסלים). עבור כל בלוק, הוא מחפש בפריים הקודם את הבלוק הדומה ביותר (למשל, בעל סכום ההפרשים המוחלטים הנמוך ביותר). ההיסט בין שני הבלוקים הללו נקרא 'וקטור תנועה'.
- פיצוי תנועה: לאחר מכן, הוא בונה גרסה 'מפצה-תנועה' של הפריים הקודם על ידי הזזת הבלוקים בהתאם לווקטורי התנועה שלהם.
- סינון: לבסוף, הוא מבצע את המיצוע הטמפורלי בין הפריים הנוכחי לבין הפריים הקודם החדש, מפצה-התנועה.
בדרך זו, אובייקט נע מתמזג עם עצמו מהפריים הקודם, ולא עם הרקע שהוא חשף זה עתה. זה מפחית באופן דרסטי ארטיפקטים של מריחה. מימוש הערכת תנועה הוא עתיר חישובים ומורכב, ולעיתים קרובות דורש אלגוריתמים מתקדמים, והוא כמעט תמיד משימה עבור WebAssembly או אפילו compute shaders של WebGPU.
סינון אדפטיבי
שיפור נוסף הוא להפוך את המסנן לאדפטיבי. במקום להשתמש בערך alpha
קבוע לכל הפריים, ניתן לשנות אותו בהתבסס על תנאים מקומיים.
- אדפטיביות לתנועה: באזורים עם תנועה מזוהה גבוהה, ניתן להגדיל את
alpha
(למשל, ל-0.95 או 1.0) כדי להסתמך כמעט לחלוטין על הפריים הנוכחי, ולמנוע כל טשטוש תנועה. באזורים סטטיים (כמו קיר ברקע), ניתן להקטין אתalpha
(למשל, ל-0.5) להפחתת רעש חזקה הרבה יותר. - אדפטיביות לבהירות (Luminance): רעש נראה לעיתים קרובות יותר באזורים כהים של התמונה. ניתן להפוך את המסנן לאגרסיבי יותר בצללים ופחות אגרסיבי באזורים בהירים כדי לשמר פרטים.
מקרי שימוש ויישומים מעשיים
היכולת לבצע הפחתת רעש באיכות גבוהה בדפדפן פותחת אפשרויות רבות:
- תקשורת בזמן אמת (WebRTC): עיבוד מקדים של פיד מצלמת הרשת של המשתמש לפני שהוא נשלח למקודד הווידאו. זהו יתרון עצום לשיחות וידאו בסביבות תאורה נמוכה, המשפר את האיכות החזותית ומפחית את רוחב הפס הנדרש.
- עריכת וידאו מבוססת-רשת: הצעת מסנן 'Denoise' כתכונה בעורך וידאו בדפדפן, המאפשר למשתמשים לנקות את קטעי הווידאו שהעלו ללא עיבוד בצד השרת.
- גיימינג בענן ושולחן עבודה מרוחק: ניקוי זרמי וידאו נכנסים כדי להפחית ארטיפקטים של דחיסה ולספק תמונה ברורה ויציבה יותר.
- עיבוד מקדים לראייה ממוחשבת: עבור יישומי AI/ML מבוססי-רשת (כמו מעקב אחר אובייקטים או זיהוי פנים), הפחתת רעש בווידאו הקלט יכולה לייצב את הנתונים ולהוביל לתוצאות מדויקות ואמינות יותר.
אתגרים וכיוונים עתידיים
למרות עוצמתה, לגישה זו יש אתגרים. מפתחים צריכים להיות מודעים ל:
- ביצועים: עיבוד בזמן אמת לווידאו HD או 4K הוא תובעני. מימוש יעיל, בדרך כלל עם WebAssembly, הוא חובה.
- זיכרון: אחסון של פריים קודם אחד או יותר כמאגרים לא דחוסים צורך כמות משמעותית של RAM. ניהול זהיר הוא חיוני.
- זמן השהיה (Latency): כל שלב עיבוד מוסיף זמן השהיה. עבור תקשורת בזמן אמת, צינור זה חייב להיות ממוטב ביותר כדי למנוע עיכובים מורגשים.
- העתיד עם WebGPU: ה-WebGPU API המתפתח יספק חזית חדשה לסוג זה של עבודה. הוא יאפשר לאלגוריתמים אלו לכל פיקסל לרוץ כ-compute shaders מקביליים ביותר על ה-GPU של המערכת, ויציע קפיצת דרך נוספת ועצומה בביצועים, אפילו מעל WebAssembly על ה-CPU.
סיכום
ה-WebCodecs API מסמן עידן חדש לעיבוד מדיה מתקדם ברשת. הוא הורס את המחסומים של אלמנט ה-<video>
המסורתי, שנתפס כקופסה שחורה, ומעניק למפתחים את השליטה המדויקת הדרושה לבניית יישומי וידאו מקצועיים באמת. הפחתת רעש טמפורלית היא דוגמה מושלמת לכוחו: טכניקה מתוחכמת שמתמודדת ישירות הן עם האיכות הנתפסת על ידי המשתמש והן עם היעילות הטכנית הבסיסית.
ראינו שעל ידי יירוט אובייקטי VideoFrame
בודדים, אנו יכולים ליישם לוגיקת סינון עוצמתית כדי להפחית רעש, לשפר את יכולת הדחיסה ולספק חווית וידאו מעולה. בעוד שמימוש JavaScript פשוט הוא נקודת פתיחה נהדרת, הדרך לפתרון מוכן לייצור ובזמן אמת עוברת דרך הביצועים של WebAssembly, ובעתיד, כוח העיבוד המקבילי של WebGPU.
בפעם הבאה שתראו וידאו מגורען ביישום רשת, זכרו שהכלים לתקן זאת נמצאים כעת, לראשונה, ישירות בידי מפתחי הרשת. זהו זמן מרגש לבנות עם וידאו באינטרנט.