Skip to content

Instantly share code, notes, and snippets.

@Miniontoby
Last active November 21, 2025 11:24
Show Gist options
  • Select an option

  • Save Miniontoby/fcc1623191dd3792e26f22a046c08bac to your computer and use it in GitHub Desktop.

Select an option

Save Miniontoby/fcc1623191dd3792e26f22a046c08bac to your computer and use it in GitHub Desktop.
Key and Fill Source filters for HTML/CSS page content

Background Information

Let's start with some background information, so you'll get my point of view.

So I've been working on a content projection software, for lyrics, pictures, videos, pdf's, countdown and clock. Since it would be used on beamers, but also for livestream subtitles, I also implemented a greenscreen preset.

The other day I helped out on a big conference as end of director for the livestream. For the lower thirds for nameplates and stuff we used ProPresenter sending out TWO feeds. One with black/white and one with the actual content. It didn't work that well, as it kept on sending the full color to both outputs, unless our technician came back to fix it again. (by just switching the mode to off and then on again...)

Anyways, I found it very interesting as it made it possible to use all the colors of the world inside your content, without having to worry about the greenscreen filter removing the green of your image.

It turned out the technique is called Key and Fill sourcing.

  • The key source shows a black to white (grayscale) representation of the alpha/transparency channel of the colors. Where black will be that there's no content/transparent and white will be solid colors/not transparent.
  • The fill source shows the actual full color content that you want to show. No alpha/transparency applied.

Then each source is sent over a cable (for example HDMI or SDI) and then inputted into a video mixer, like an Atem Extreme, Atem Constellation or into software like vMix. Then the switcher takes both inputs and applies the key to the fill, so only the content you want to show (for example a nameplate in lower thirds), will be overlayed onto your other inputs/camera's/content.

So yeah, an overlay without needing to worry about greenscreen messing up the color green, sounds AWESOME.


The process

Most of the time people use a UltraStudio HD Mini to get a key and fill source for their content. However, I do not own an UltraStudio and I don't want to buy it just for this project.

And since I wrote the content projection software myself, I am able to add whatever features I want. It is written with HTML/css/javascript, I started to search for CSS filters on google, stackoverflow, etc.

But I couldn't find anything online. No good example code that fitted my usecase, nothing.

Well I figured I could use filter: brightness(5); to make all the visible content white, but it didn't allow for half transparent stuff. Also it didn't really work with some specific setups, where background colors of elements wouldn't be filtered.

So yeah, with no choice left for me, I went to Claude AI and asked it this:

Yo claude, I am stuck with a little html css javascript project of mine. I want to make a key/fill source system. So both pages show the same content (already got that, no worries). But I want to be able to make the key source page to convert the alpha channel of the elements into the brightness of the things. I've tried filter: brightness(5) which makes all the elements white. But like yeah it doesn't account for alpha channel of stuff. Any tips on how to make this happen?

And Claude responded with:

You need a CSS filter that maps alpha to luminance. The trick is using a color matrix filter. The matrix takes the alpha channel (4th column) and maps it to RGB, making transparent = black and opaque = white. Put the SVG anywhere in your key page's HTML, then apply the filter to your container element.

I tried out the code he gave me and it worked flawlessly. He made me a key source that worked first try.

Of course html content that don't fill up the full height of the window, will only get the filter applied for only the visible content. So the background color of the page, e.g. white by light mode still shows, and it will mess up the key/fill. And I wasn't sure if it would work for all platforms and all browsers, as it looked like some wizardy stuff he used.

So I prompted:

Thanks! It works. However I did notice that is the content of the page doesn't fill the whole page, the normal background color of the page (light mode would make it white) still shows... Also on how many browsers is this filter -> feColorMatrix supported?

Claude responded with:

The unfilled area showing through is expected - the filter only affects elements it's applied to, not the page background itself. Fix it by setting the body/html background to black. Browser support for SVG filters is excellent - works in all modern browsers (Chrome, Firefox, Safari, Edge) back to IE10. feColorMatrix specifically has been around since SVG 1.1, so you're solid on compatibility.

And I was like: Wow! That's amazing. But of course I wasn't done yet, as this was only the key source. And the fill one still had half transparent elements. Which just doesn't look awesome, as the colors look different when they're half transparent.

So I asked Claude:

What should I do with the fill source? It does still have alpha applied as well, which doesn't make the colors look full.

Claude responded with:

You're right - premultiplied alpha can mess with the composite. For the fill source, you want to remove the alpha channel entirely. Use the same SVG filter technique but with a different matrix This keeps RGB as-is but forces alpha to 1 (fully opaque). Apply it to your fill page's container.

And guess what, it worked!


Why?

So why did I post all of this conversation on here? Well I wanted to post this solution so people with the same issue don't have to use more liters of water for AI to regenerate this solution. I want to provide snippets where there are no snippets yet. And yeah, I am against the use of AI, but sometimes there's no way to get your info. And since this just works, and is supported even for IE10, then this might as well be the best solution.


Usage

Now you're wondering, how do I use this? It's quite simple actually.

Okey, so you must have two HTML pages with the same content, but different files. (Or in my case you needed a synchronised system where the content would be dynamicially updated across all pages/windows)

When you have that, you must do two things per page:

Key source page

Add the SVG element for the keysource into your HTML page. It doesn't matter where you put it. You could just put it inside the body at the very end, it doesn't matter.

And then you must edit your stylesheet; wether its a style.css or just a <style> element. You must add the following:

body{background-color:black;filter:url(#alpha-to-brightness)}

Why the background-color black?

Otherwise if your page doesn't fill up the whole screen, then it will show white, which will mess up the system.

After doing so, just save all your files and check it out! If it doesn't work, check if you have done everything correctly and inspect if the filter url is seen as valid by the browser.

Fill source page

Add the SVG element for the fill source into your HTML page, just like the Key source one. Again it doesnt matter where you put it, as long as its on the page.

And then you must edit your stylesheet; wether its a style.css or just a <style> element. You must add the following:

body{filter:url(#remove-alpha)}

Here it doesn't matter what the background color is set to.

Are you using a framework and not two seperate files?

If you're using like Svelte, React, Vue, etc. then you might not be using two seperate files, but just conditional based content. You can add the Key And Fill source SVG element into your page then and then just let your frontend code switch between the two filters.


Questions?

If you have questions, you can ask, but I'm unsure if I can answer them

<svg style="position: absolute; width: 0; height: 0;">
<defs>
<filter id="alpha-to-brightness">
<feColorMatrix type="matrix"
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1"/>
</filter>
</defs>
</svg>
body {
background-color: black;
filter: url(#alpha-to-brightness);
}
<svg style="position: absolute; width: 0; height: 0;">
<defs>
<filter id="remove-alpha">
<feColorMatrix type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0 1"/>
</filter>
</defs>
</svg>
body {
filter: url(#remove-alpha);
}
<svg style="position: absolute; width: 0; height: 0;">
<defs>
<filter id="alpha-to-brightness">
<feColorMatrix type="matrix"
values="0 0 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1"/>
</filter>
<filter id="remove-alpha">
<feColorMatrix type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0 1"/>
</filter>
</defs>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment