Video with Alpha Transparency on the Web
jakearchibald.com
jakearchibald.com
x265 has added support for alpha very recently, but only using their standalone CLI. https://bitbucket.org/multicoreware/x265_git/commits/c8c9d22...
[1] https://www.mux.com/blog/your-browser-and-my-browser-see-dif...
How do we bring this to the attention of the people working on these decoders and renderer pipelines?
I feel like I should re-run your experiments with photos now, especially taking into account the unusual colorspaces used there, like XYB:
The first video was the content I desired, the second video was solid white but for the same length of time as the first video. I was honestly a little flummoxed about the white stream for about 15 seconds before it hit me "this must be an alpha channel".
I don't suspect I have ever seen it in action, so I am extremely curious how well alpha channels can possibly turn out with lossy video formats where the result of any given frame is a matter of interpretation? In lossless formats like GIF the borders of objects at any given frame are perfectly defined, but lossy formats, especially ones using discrete cosine transform, where the object ends and background begins is not clear cut.
From my testing VP9 videos with transparency are fine if you aren't stingy with bitrate, and in general if source material isn't CGI things will be crusty at edges anyway (e.g. greenscreen with motion blur).
What's that?
a statement that the GIF image file format is limited to 256 colors is simply false.
https://web.archive.org/web/20140908023322/http://phil.ipal....How is pretty sneaky.
(still feels very stupid to have to do it but until Google Docs lets you embed a silent auto playing mp4…)
A gif is lossless in that it does not lose data. When you define a frame within the formats limitations, you will always receive that exact frame in return.
The conversion might have lost data but that's not the gif losing it. That's just what happens trying to fit a larger domain in a smaller domain.
The individual frames within a gif are bitmaps that do not lose detail across saves, extractions, rotations, etc. Each individual pixel is displayed exactly as originally defined.
Compare this to JPEG or MP4 where your individual pixel becomes part of a larger cosine field. There is no guarantee this pixel will exist and even if it does, its exact value is almost certainly changed. This is what "lossy" is.
Only assuming your base images have that many bits of information.
Most cameras max out at 16 bits per channel when shooting RAW, and even those rarely have mostly noise in the lower bits.
I'm sure you can find an example of some supercooled telescope sensor that actually does capture 16 bits per channel, maybe even more.
In the real world your image source is usually either JPG (24 bits per pixel, already debayered and already lossy) or RAW (16 bits per pixel max, bayered).
A blurry alpha channel is just having soft edges. A naive encoder will cause some of the previously transparent background bleed into visible pixels, usually causing darkened halos around the edges. This is a common problem in video game assets, and the fixes are the same: either bleed color around the edges (make transparent pixels have the right RGB values), or use premultiplied alpha color space. AVIF (also in theory) supports marking images as having premultiplied alpha.
Also premultiplication is just a way to cache the multiplication operation:
// using normal image
// note that everything after the plus depends entirely on the image
out = current * (1-img.a) + vec4(img.r*img.a, img.g*img.a, img.b*img.a, img.a)
// using premultiplied image
out = current * (1-img.a) + img
Which might make sense if you're drawing same bitmap very many times.However, in many cases, the requirement isn't actually transparency, the requirement is a video background seamless with the containing page.
With most pages white or off-white, this can be done at production. Even responsive dark mode can be done in production if two clips are made.
We used this simpler technique for borderless animated video (e.g., a streaming video spokesperson walking across your e-commerce product page) 20 years ago.
The optical illusion of borderless transparency works so surprisingly well it's unbelievable it's not seen widely.
I can't find the original bug report (this was around 2018 and it was already pretty old, like 5+ years), but at the time they were refusing to fix it even though the choice of color profile went against the CSS spec.
Edit: Because Chromium, not Chrome. Came right up just switching to that, the bug report was from 2010, and from the recent comments looks like it's still an issue:
Migrated Chromium bugs? https://issues.chromium.org/issues/40401125
The original page I remember from back then, linked from above: https://bugs.chromium.org/p/chromium/issues/detail?id=44872&...
One way to ensure this is layout using, ahem, tables. (Don't laugh, it works for the site you're reading right now...)
Not everything we've come up with in the last 20 years made life easier...
It will take transparent MP4/MOV as input and output compatible versions for Safari (HEVC) and Chrome (WEBM). Then you simply use a tag that includes both e.g:
<video playsinline preload="metadata">
<source src="video.mp4" type="video/mp4; codecs=hvc1">
<source src="video.webm" type="video/webm">
</video>avconvert -p PresetHEVCHighestQualityWithAlpha -s ./input.mov -o ./output.mov
is the trick
avconvert gives you almost no control over the output. You'll end up with a file orders of magnitude bigger than the alternatives covered in the article.
Compressor is the next best thing, as it gives you some control, but you still end up with a file multiples bigger than the ideal solution.
Like what exactly? I played/viewed a lot of Flash content back in the day, but I don't remember anything out of the ordinary that simply isn't possible/is extremely difficult today, want to jog my memory?
There was a period of time after Flash was deprecated and web technologies weren't ready to replace it, but I think we're beyond that today, unless I'm missing something essential Flash could do.
But, hard to know exactly how it was implemented without seeing the source, so I guess we'll never know.
> Flash allowed to create web experience that is extremely difficult to do with modern technologies
Which sounds like it's about the output, not the process for getting to the output.
Instead of citing a particular technology (whether Flash, HTML5, WebGPU, etc), which risks getting into the weeds of defending any shortcomings in one or the other, I'd rather propose that client-side rendering in general is still under-utilized and capable of so much more. I also think the under-appreciated elephant in the room is that Apple and Google have both been guilty of subtly manipulating web standards processes to nerf client-side applications. And they've been very clever about it. Sometimes proposing potentially exciting enhancements but later sabotaging them - always with some plausibly deniable cover, whether security, backward compatibility, accessibility, etc. Other times they'll 'embrace and extend' a new proposal - and then keep extending and extending until it either becomes practically unimplementable, unperformant, bloated or just collapses in committee under it's own gravity.
Bottom line: powerful, performant client-side applications securely delivered in real-time through the open web are bad for app store walled-garden models and businesses that rely on controlling centralized social media or gaming platforms. Advanced client-side technologies and standards aren't as good or widely deployed as they could be because powerful interests don't want them to be.
They say it works in Chromium, it's buggy in Firefox, and slow in Safari.
https://apng.onevcat.com/demo/
Or animated webp?
https://mathiasbynens.be/demo/animated-webp
https://cobalt.googlesource.com/cobalt/+/refs/tags/trunk.unn...
Webp is the same, only a little bit better.
In both cases you need all the frames as complete pictures after all.
Animated WebP is based on VP8. However, the alpha channel is stored losslessly, which makes compression even worse than VP8.
VP8 is of course a generation behind VP9, so the results will be significantly worse than the VP9 example in the article, which is significantly worse than AV1.
There are also playback issues in Safari.
-- Continues to render perfectly fine and smooth with 60 fps on my Windows/Firefox/Thinkpad
Ah, the problems of good software running on high-powered machines. Closed, bug not reproducable!
https://static.crowdwave.link/transparentanimatedwebp.html
Seems to work on Chrome, Firefox and Safari.
The video file is here: https://static.crowdwave.link/transparentanimatedwebp.webp
I put the result of the code below here:
This python program will generate webp frames and if you have ffmpeg will convert them to transparent animated webp:
from PIL import Image, ImageDraw
import random
import subprocess
# Image parameters
width, height = 640, 480
frames = 1000
shapes = 10
def random_color():
# Generate a random RGBA color with varying levels of transparency (alpha)
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(50, 200))
def random_position():
return random.randint(0, width - 80), random.randint(0, height - 80)
def random_size():
return random.randint(20, 80)
def create_gradient(width, height):
base = Image.new('RGBA', (width, height))
top = Image.new('RGBA', (width, height))
for y in range(height):
alpha = int((y / height) * 255) # Alpha varies from 0 to 255
for x in range(width):
top.putpixel((x, y), (255, 255, 255, alpha)) # White gradient
return Image.alpha_composite(base, top)
# Create a series of images
for i in range(frames):
# Create an image with a transparent gradient background
img = create_gradient(width, height)
draw = ImageDraw.Draw(img)
for _ in range(shapes):
shape_type = random.choice(['rectangle', 'ellipse'])
x1, y1 = random_position()
x2, y2 = x1 + random_size(), y1 + random_size()
color = random_color()
if shape_type == 'rectangle':
draw.rectangle([x1, y1, x2, y2], fill=color)
elif shape_type == 'ellipse':
draw.ellipse([x1, y1, x2, y2], fill=color)
# Save each frame as a PNG file
img.save(f'frame_{i:04d}.png')
print("Frames created successfully.")
# Create an animated WebP from the generated frames
subprocess.run([
'ffmpeg', '-y', '-i', 'frame_%04d.png', '-vf', 'fps=30,scale=320:-1:flags=lanczos',
'-loop', '0', '-pix_fmt', 'yuva420p', 'output.webp'
])
print("Animated WebP created successfully.")
Then put the output.webp and this index.html on your local disk somewhere and load the index.html in a browser.Pull the slider to change the background so you can see it is transparent.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animated WebP Display with Background Image</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-image: url('transparentanimatedwebp.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
img {
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
<img src="transparentanimatedwebp.webp" alt="Animated WebP">
</body>
</html>Edit: also no desktop Safari support for transparent animated WebP.
>> Edit: also no desktop Safari support for transparent animated WebP.
Do you mean the link above that I posted? Works fine in my desktop Safari.
VP8 is of course a generation behind VP9, so the results will be significantly worse than the VP9 example in the article, which is significantly worse than AV1.
Also, the demo crashes Safari for me (it plays progressively slower and slower).
I had to revert back to autoplaying no-audio MP4 files for this reason.
If you can already have an access to WebGL shaders, probably banding can be fixed in shaders too.
Edit: sorry, ProRes is of course not "on the web".
(Un)surprised to know this is still an issue in 2024.