Введение
Отображение видеоконтента с прозрачностью является востребованной задачей в современных веб-приложениях. Анимированные элементы интерфейса, стикеры, оверлеи и визуальные эффекты требуют корректного композитинга видео поверх произвольного фона. Данная функциональность особенно важна в следующих сценариях применения:
- анимированные иконки и индикаторы загрузки, которые должны органично интегрироваться в дизайн страницы независимо от цвета фона;
- видеопревью продуктов в электронной коммерции, где товар должен отображаться на динамически меняющемся фоне;
- наложение визуальных эффектов в браузерных видеоредакторах, требующих точного контроля над прозрачностью;
- интерактивные презентации и обучающие материалы с анимированными персонажами и объектами.
Несмотря на то, что видеоформаты поддерживают альфа-канал уже более пятнадцати лет, практическое использование этой возможности в браузерах сопряжено с существенными ограничениями. Различия в поддержке кодеков между браузерами, артефакты отображения на отдельных платформах, низкая производительность декодирования и значительный размер файлов вынуждают разработчиков прибегать к неэффективным решениям: GIF-анимации с ограничением в 256 цветов, последовательностям PNG-изображений с большим объёмом данных или статичным CSS-спрайтам.
Цель данного исследования – систематизировать существующие подходы к отображению видео с прозрачностью, провести сравнительный анализ эффективности различных кодеков, выявить ограничения каждого метода и предложить эффективное кроссбраузерное решение с использованием технологий WebGL, SVG-фильтров и WebGPU.
Объекты и методы исследования
В качестве объектов исследования рассматривались современные видеоформаты с поддержкой альфа-канала: Animated AVIF, VP9 с прозрачностью, HEVC с альфа-каналом, а также альтернативные подходы – GIF, Animated WebP, последовательности PNG. Дополнительно анализировался предлагаемый метод Stacked Alpha с использованием кодеков H.264, VP9, AV1 и HEVC без нативной поддержки прозрачности.
Эксперименты проводились в браузерах Google Chrome версии 125, Mozilla Firefox версии 126 и Apple Safari версии 17.4 на платформах macOS Sonoma, Windows 11 и iOS 17. Для оценки производительности измерялось время рендеринга одного кадра, загрузка графического процессора и стабильность частоты кадров при воспроизведении.
Тестовый видеоролик имел следующие характеристики: разрешение 1920×1080 пикселей, длительность 10 секунд, частота кадров 30 fps. Содержимое представляло собой анимированный элемент пользовательского интерфейса с градиентными полупрозрачными областями, что позволяло оценить качество обработки сложных альфа-масок.
Нативная поддержка прозрачности
Результаты анализа поддержки видеоформатов с прозрачностью в современных браузерах представлены в таблице 1.
Таблица 1
Поддержка видеоформатов с нативной прозрачностью в браузерах
Формат | Chrome | Firefox | Safari | iOS | Проблемы |
Animated AVIF | Частично | Частично | Нет | Нет | < 10 fps, артефакты |
VP9 + Alpha | Да | Да | Нет | Нет | Нет поддержки Safari |
HEVC + Alpha | Нет | Нет | Да | Да | Только Apple |
Animated WebP | Да | Да | Артефакты | Артефакты | Нет контроля кадров |
GIF | Да | Да | Да | Да | 256 цветов, 1-bit alpha |
Формат Animated AVIF, построенный на кодеке AV1, теоретически обеспечивает наилучшее сжатие с поддержкой прозрачности. Однако Safari полностью не поддерживает прозрачность в анимированных AVIF [1], а производительность декодирования в Chrome и Firefox недостаточна для воспроизведения 60 fps даже на современных устройствах [2]. В ходе тестирования на MacBook Pro M3 частота кадров составила 8–12 fps.
Кодек VP9 с альфа-каналом обеспечивает хорошее качество при умеренном размере файла и поддерживается в Chrome и Firefox. Однако Safari не поддерживает прозрачность в VP9 [3], что исключает использование на всех устройствах Apple, составляющих значительную долю мобильного рынка.
HEVC с альфа-каналом является проприетарным расширением Apple [4] и работает только на устройствах этой компании. Кодирование возможно исключительно на macOS с использованием приложения Apple Compressor стоимостью около 50 долларов США. Кроссплатформенное кодирование через FFmpeg даёт файлы в 2-3 раза большего размера из-за использования неоптимального пиксельного формата BGRA.
Формат Animated WebP поддерживается во всех современных браузерах, однако Safari на iOS демонстрирует визуальные артефакты на границах полупрозрачных областей в виде характерных «ореолов». Кроме того, WebP отображается через элемент <img>, что исключает программный контроль воспроизведения: невозможны пауза, перемотка к конкретному кадру и синхронизация с другими элементами интерфейса.
Сравнение эффективности кодеков
Для оценки эффективности различных кодеков был проведён эксперимент по кодированию тестового видеоролика в формате Stacked Alpha (видео двойной высоты с альфа-каналом в нижней половине). Результаты представлены в таблице 2.
Таблица 2
Сравнение размера файлов для различных кодеков (тестовое видео 1080p, 10 сек)
Кодек/Формат | Контейнер | Размер | Поддержка | Примечания |
H.264 (AVC) | MP4 | 1.24 MB | 99% | Лучшая совместимость |
VP9 | WebM | 0.89 MB | 94% | Нет iOS Safari |
AV1 | MP4 | 0.52 MB | 78% | Нет старых устройств Apple |
HEVC (H.265) | MP4 | 0.71 MB | 85% | Требует лицензию |
VP9 + Alpha | WebM | 1.42 MB | 72% | Нативная прозрачность |
HEVC + Alpha | MOV | 2.85 MB | 32% | Только Apple |
Анализ результатов показывает, что кодек H.264 в контейнере MP4 обеспечивает наилучшую совместимость (99% браузеров) при приемлемом размере файла. Кодек AV1 даёт наименьший размер (на 58% меньше H.264), однако не поддерживается на устройствах Apple старше iPhone 15 Pro и MacBook с чипом M3.
Overhead видео двойной высоты
Важным вопросом является оценка накладных расходов (overhead) при использовании видео двойной высоты вместо нативной прозрачности. Альфа-канал в нижней половине кадра представляет собой одноканальное изображение (grayscale), которое эффективно сжимается видеокодеками благодаря:
- отсутствию цветовой информации (только яркость);
- высокой пространственной корреляции (большие области одинаковой прозрачности);
- высокой временной корреляции (альфа-маска редко меняется между кадрами).
Экспериментальные измерения показали, что альфа-часть добавляет от 5% до 15% к размеру файла в зависимости от сложности маски. Для тестового видео с UI-анимацией overhead составил 8.2%. При этом размер Stacked Alpha видео (H.264, 1.24 MB) на 13% меньше, чем VP9 с нативной прозрачностью (1.42 MB), и на 56% меньше, чем HEVC с альфа-каналом (2.85 MB).
Рекомендуемая стратегия выбора кодека: H.264/MP4 в качестве основного формата для максимальной совместимости с fallback на AV1/MP4 для современных браузеров, что позволяет сократить трафик на 50–60% для большинства пользователей.
Результаты и их обсуждение
Концепция Stacked Alpha
Предлагаемый подход основан на кодировании альфа-канала непосредственно в видеопоток без использования нативной поддержки прозрачности кодеком. Исходный кадр с прозрачностью преобразуется в кадр двойной высоты, где верхняя половина содержит RGB-данные, а нижняя – альфа-канал, представленный как яркость (grayscale). На рисунке 1 показано исходное изображение с альфа-каналом.

Рис. 1. Исходное изображение с альфа-каналом (радужный градиент с мягкими краями, 300×300 пикселей)
На рисунке 2 представлено преобразованное изображение в формате Stacked Alpha. Верхняя половина содержит RGB-данные без прозрачности (альфа-канал установлен в 255), нижняя половина – альфа-канал исходного изображения, преобразованный в оттенки серого, где белый цвет соответствует полной непрозрачности, чёрный – полной прозрачности.

Рис. 2. Формат Stacked Alpha: RGB-данные (верхняя половина) и альфа-канал как grayscale (нижняя половина), 300×600 пикселей
Математическая модель композитинга
Для восстановления исходного изображения с прозрачностью из формата Stacked Alpha выполняется преобразование текстурных координат и смешивание каналов. Пусть (u, v) – нормализованные координаты результирующего пикселя, где u, v ∈ [0, 1]. Координаты выборки из текстуры видеокадра определяются следующим образом:
Для получения RGB-компонент (верхняя половина кадра): (u_color, v_color) = (u, v × 0.5)
Для получения альфа-компоненты (нижняя половина кадра): (u_alpha, v_alpha) = (u, 0.5 + v × 0.5)
Результирующий цвет с учётом premultiplied alpha для корректного композитинга: C_out = (R × α, G × α, B × α, α), где α = sample(u_alpha, v_alpha).
Реализация на WebGL с общим контекстом
Ключевое отличие предлагаемой реализации от существующих решений [5] – использование единого глобального WebGL-контекста для всех видеоэлементов на странице. Это устраняет ограничение браузеров на количество WebGL-контекстов (8–16 в зависимости от платформы) и позволяет отображать неограниченное число видео с прозрачностью.
Архитектура решения включает следующие компоненты: один скрытый WebGL canvas для рендеринга, отдельный 2D canvas для каждого видеоэлемента, шейдерную программу для композитинга и менеджер, координирующий рендеринг всех видео. В листинге 1 представлен код шейдеров.
Листинг 1. Вершинный и фрагментный шейдеры WebGL
// Вершинный шейдер
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
}
// Фрагментный шейдер
precision mediump float;
uniform sampler2D u_frame;
varying vec2 v_texCoord;
void main() {
// Координаты для RGB (верхняя половина)
vec2 colorCoord = vec2(v_texCoord.x, v_texCoord.y * 0.5);
// Координаты для альфы (нижняя половина)
vec2 alphaCoord = vec2(v_texCoord.x, 0.5 + v_texCoord.y * 0.5);
vec4 color = texture2D(u_frame, colorCoord);
float alpha = texture2D(u_frame, alphaCoord).r;
// Premultiplied alpha для корректного композитинга
gl_FragColor = vec4(color.rgb * alpha, alpha);
}
В листинге 2 представлена инициализация глобального WebGL-контекста и создание шейдерной программы.
Листинг 2. Инициализация глобального WebGL-контекста
// Глобальный контекст – создаётся один раз при загрузке модуля
const glCanvas = document.createElement("canvas");
const gl = glCanvas.getContext("webgl", {
alpha: true,
premultipliedAlpha: true,
preserveDrawingBuffer: true
});
// Компиляция шейдеров и создание программы
const vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, VERTEX_SHADER_SOURCE);
gl.compileShader(vertShader);
const fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, FRAGMENT_SHADER_SOURCE);
gl.compileShader(fragShader);
const program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
gl.useProgram(program);
// Создание буферов для fullscreen quad
const positions = new Float32Array([-1,-1, 1,-1, -1,1, 1,1]);
const texCoords = new Float32Array([0,1, 1,1, 0,0, 1,0]);
// ... привязка атрибутов
В листинге 3 показано решение ключевой проблемы – использование одного WebGL canvas для рендеринга множества видео с копированием результата на отдельные 2D canvas.
Листинг 3. Менеджер рендеринга с общим WebGL-контекстом
class AlphaVideoRenderer {
constructor() {
this.videos = new Map(); // video -> { canvas2d, texture }
}
register(video, canvas2d) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
this.videos.set(video, { canvas2d, texture });
this.startLoop(video);
}
startLoop(video) {
const render = () => {
if (video.paused || video.ended) return;
this.renderFrame(video);
video.requestVideoFrameCallback(render);
};
video.requestVideoFrameCallback(render);
}
renderFrame(video) {
const { canvas2d, texture } = this.videos.get(video);
const W = video.videoWidth;
const H = video.videoHeight / 2;
// Изменяем размер WebGL canvas под текущее видео
glCanvas.width = W;
glCanvas.height = H;
gl.viewport(0, 0, W, H);
// Загружаем кадр видео в текстуру
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
gl.RGBA, gl.UNSIGNED_BYTE, video);
// Рендерим с шейдером композитинга
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// Копируем результат на целевой 2D canvas
canvas2d.width = W;
canvas2d.height = H;
canvas2d.getContext("2d").drawImage(glCanvas, 0, 0);
}
}
// Использование
const renderer = new AlphaVideoRenderer();
renderer.register(videoElement, outputCanvas);
Реализация на SVG-фильтрах
SVG-фильтры предоставляют альтернативный способ композитинга, выполняемый на GPU. Преимущество данного подхода – отсутствие ограничений на количество элементов и возможность работы без JavaScript в простых сценариях. Реализовано два варианта: встраивание видео непосредственно в SVG и применение CSS-фильтра к canvas.
Вариант 1: Видео внутри SVG
В данном варианте элемент <video> размещается внутри SVG через <foreignObject>, а фильтр применяется средствами SVG. Листинг 4 демонстрирует структуру разметки.
Листинг 4. SVG с встроенным видео и фильтром
<svg width="400" height="300" viewBox="0 0 400 300">
<defs>
<!-- Фильтр: извлекает R-канал в альфу -->
<filter id="extractAlpha" color-interpolation-filters="sRGB">
<feColorMatrix type="matrix"
values="0 0 0 0 1
0 0 0 0 1
0 0 0 0 1
1 0 0 0 0"/>
</filter>
<!-- Маска из нижней половины видео -->
<mask id="alphaMask">
<svg viewBox="0 300 400 300" width="400" height="300">
<foreignObject width="400" height="600">
<video xmlns="http://www.w3.org/1999/xhtml"
src="stacked.mp4" autoplay muted loop
style="filter:url(#extractAlpha)"/>
</foreignObject>
</svg>
</mask>
</defs>
<!-- Верхняя половина видео с маской -->
<svg viewBox="0 0 400 300" mask="url(#alphaMask)">
<foreignObject width="400" height="600">
<video xmlns="http://www.w3.org/1999/xhtml"
src="stacked.mp4" autoplay muted loop/>
</foreignObject>
</svg>
</svg>
Матрица feColorMatrix выполняет преобразование R-канала во все цветовые компоненты и в альфа-канал. Результат: R' = G' = B' = 1 (белый цвет), A' = R (исходная яркость нижней половины становится прозрачностью маски).
Вариант 2: CSS-фильтр на Canvas
Альтернативный подход использует CSS-свойство filter для применения SVG-фильтра к элементу canvas, на который покадрово рисуется видео. Этот метод обеспечивает лучшую совместимость с различными браузерами. Листинг 5 демонстрирует реализацию.
Листинг 5. CSS-фильтр SVG на Canvas с покадровым рендерингом видео
<!-- SVG фильтр в документе -->
<svg style="position:absolute; width:0; height:0;">
<defs>
<filter id="alphaComposite" color-interpolation-filters="sRGB">
<!-- Смещаем изображение вверх на 50% для получения альфы -->
<feOffset in="SourceGraphic" dy="-50%" result="shifted"/>
<!-- Извлекаем R-канал как альфу -->
<feColorMatrix in="shifted" type="matrix" result="alphaMask"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0"/>
<!-- Накладываем альфу на исходник -->
<feComposite in="SourceGraphic" in2="alphaMask"
operator="in"/>
</filter>
</defs>
</svg>
<canvas id="output" style="filter: url(#alphaComposite)"></canvas>
<script>
const video = document.querySelector("video");
const canvas = document.getElementById("output");
const ctx = canvas.getContext("2d");
function renderLoop() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight; // Полная высота
ctx.drawImage(video, 0, 0);
video.requestVideoFrameCallback(renderLoop);
}
video.requestVideoFrameCallback(renderLoop);
</script>
Реализация на WebGPU
WebGPU представляет современный низкоуровневый API для работы с графическим процессором, обеспечивающий лучшую производительность по сравнению с WebGL. API не имеет ограничений на количество контекстов и предоставляет явный контроль над ресурсами GPU. Листинг 6 демонстрирует минимальную реализацию.
Листинг 6. Реализация композитинга на WebGPU
// Инициализация WebGPU
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = canvas.getContext("webgpu");
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format, alphaMode: "premultiplied" });
// WGSL шейдер
const shaderModule = device.createShaderModule({ code: `
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
var pos = array<vec2f,4>(vec2f(-1,-1),vec2f(1,-1),vec2f(-1,1),vec2f(1,1));
return vec4f(pos[i], 0, 1);
}
@group(0) @binding(0) var tex: texture_external;
@group(0) @binding(1) var samp: sampler;
@fragment fn fs(@builtin(position) pos: vec4f) -> @location(0) vec4f {
let uv = pos.xy / vec2f(textureDimensions(tex));
let colorUV = vec2f(uv.x, uv.y * 0.5);
let alphaUV = vec2f(uv.x, 0.5 + uv.y * 0.5);
let color = textureSampleBaseClampToEdge(tex, samp, colorUV);
let alpha = textureSampleBaseClampToEdge(tex, samp, alphaUV).r;
return vec4f(color.rgb * alpha, alpha);
}
` });
// Рендеринг кадра
function renderFrame(video) {
const texture = device.importExternalTexture({ source: video });
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: texture },
{ binding: 1, resource: sampler }
]
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({...});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(4);
pass.end();
device.queue.submit([encoder.finish()]);
}
Ключевое преимущество WebGPU – использование texture_external для прямого импорта видеокадра без промежуточного копирования, что существенно снижает нагрузку на шину памяти.
Сравнительный анализ методов
В таблице 3 представлены результаты сравнения трёх реализаций композитинга по ключевым метрикам.
Таблица 3
Сравнение методов композитинга Stacked Alpha
Параметр | WebGL | SVG | WebGPU |
Время рендеринга кадра | 0.8–1.2 мс | 1.5–2.5 мс | 0.4–0.7 мс |
Лимит элементов | Нет* | Нет | Нет |
Поддержка браузеров | 98% | 95% | 72% |
Сложность реализации | Средняя | Низкая | Высокая |
Работа без JS | Нет | Частично | Нет |
Загрузка GPU (1080p) | 12–18% | 15–22% | 8–12% |
* При использовании архитектуры с общим контекстом
Анализ результатов показывает, что WebGPU обеспечивает наилучшую производительность (в 1.5–2 раза быстрее WebGL), однако поддерживается только в 72% браузеров. WebGL с общим контекстом представляет оптимальный баланс между производительностью и совместимостью. SVG-фильтры уступают по производительности, но позволяют создавать простые решения без JavaScript.
Заключение
Проведённое исследование показало, что нативная поддержка прозрачности в видеоформатах на данный момент не обеспечивает кроссбраузерной совместимости и достаточной производительности. Каждый из существующих форматов (AVIF, VP9+Alpha, HEVC+Alpha, WebP) имеет критические ограничения: либо не поддерживается в отдельных браузерах, либо демонстрирует артефакты и низкую производительность.
Предложенный подход Stacked Alpha в сочетании с GPU-композитингом позволяет:
- использовать любой широко поддерживаемый видеокодек (H.264, VP9, AV1);
- сократить размер файла на 40–60% по сравнению с нативными решениями;
- обеспечить стабильное воспроизведение 60 fps на всех протестированных устройствах;
- поддерживать неограниченное количество видеоэлементов при использовании архитектуры с общим WebGL-контекстом.
Сравнительный анализ кодеков показал, что оптимальной стратегией является использование H.264/MP4 в качестве основного формата для максимальной совместимости (99% браузеров) с опциональным fallback на AV1/MP4 для современных браузеров, что позволяет сократить трафик на 50–60%.
Рекомендации по выбору метода композитинга:
- для максимальной совместимости: WebGL с общим контекстом;
- для простых сценариев без JavaScript: SVG-фильтры с CSS;
- для максимальной производительности при достаточной поддержке: WebGPU с fallback на WebGL.
Направления дальнейших исследований включают оптимизацию для мобильных устройств с ограниченными ресурсами GPU, интеграцию с Media Source Extensions для адаптивного стриминга и автоматический выбор метода композитинга на основе возможностей устройства.
.png&w=384&q=75)
.png&w=640&q=75)