# Production-ready green screen with three.js

**URL:** <https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113>\
**Category:** Questions\
**Tags:** fragment-shader, shaders\
**Created:** [February 4, 2021, 1:47am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113 "2021-02-04T01:47:29Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Henrique\_Barone](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/henrique_barone/32/16129_2.png) [@Henrique\_Barone](https://discourse.threejs.org/u/Henrique_Barone)\
**Post date:** [February 4, 2021, 1:47am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/1 "2021-02-04T01:47:29Z")

</div>

I trying to port this webgl example [Production-ready green screen in the browser](https://jameshfisher.com/2020/08/11/production-ready-green-screen-in-the-browser/) to three.js  
putting the chroma key shader in a 3d object into the scene, because the idea is interact with camera and lights.This green screen algorithm is derived from the Chroma Key filter in OBS Studio that give us a very good effect result.  
[Here is my code](https://jsfiddle.net/hbarone/qjrdwe51/26/)  
I don’t know what is missing or wrong in my code, as the browser inspector is not showing anything

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [February 4, 2021, 10:26am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/2 "2021-02-04T10:26:42Z")

</div>

I’ve quickly ported the code to the latest version of `three.js`: [https://jsfiddle.net/mpkw8yse/](https://jsfiddle.net/mpkw8yse/)

But without the video texture for simplicity.

---

<div class="post-metadata">

**Author:** ![seanwasere](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/seanwasere/32/20864_2.png) [@seanwasere](https://discourse.threejs.org/u/seanwasere)\
**Post date:** [February 4, 2021, 11:36am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/3 "2021-02-04T11:36:47Z")

</div>

Hey, I’ve created an OBS inspired webcam greenscreen example as well,  
for extra reference in case you need.  
[Webcam - Three.js Tutorials (sbcode.net)](https://sbcode.net/threejs/webcam/)

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/0/3/0375b976c6fe227bc5956e753a8282feed4edffd.jpeg)

---

<div class="post-metadata">

**Author:** ![Henrique\_Barone](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/henrique_barone/32/16129_2.png) [@Henrique\_Barone](https://discourse.threejs.org/u/Henrique_Barone)\
**Post date:** [February 5, 2021, 2:05am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/4 "2021-02-05T02:05:39Z")

</div>

Hi ,Thanks !

---

<div class="post-metadata">

**Author:** ![Henrique\_Barone](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/henrique_barone/32/16129_2.png) [@Henrique\_Barone](https://discourse.threejs.org/u/Henrique_Barone)\
**Post date:** [February 5, 2021, 2:09am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/5 "2021-02-05T02:09:31Z")

</div>

Hi, Seanwasere. It’s exactly what I was trying to do ! Thanks !! 🙂

---

<div class="post-metadata">

**Author:** ![Henrique\_Barone](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/henrique_barone/32/16129_2.png) [@Henrique\_Barone](https://discourse.threejs.org/u/Henrique_Barone)\
**Post date:** [February 18, 2021, 8:59pm UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/6 "2021-02-18T20:59:31Z")

</div>

Hello,  
First, sorry for my english 😓  
I’m trying now that the chroma key object is affected by light. I spent a lot of time studying examples of how to use onBeforeCompile, in this forum and this guide ([Extending three.js materials with GLSL](https://medium.com/@pailhead011/extending-three-js-materials-with-glsl-78ea7bbb9270)), but I still don’t quite understand how the Replace works.  
I made this fiddle using a phongMaterial to demonstrate the expected result  
[chromakey shader from webcam](https://jsfiddle.net/hbarone/yp63uewk/117/)

---

<div class="post-metadata">

**Author:** ![rudrjain](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/rudrjain/32/17462_2.png) [@rudrjain](https://discourse.threejs.org/u/rudrjain)\
**Post date:** [April 2, 2021, 5:05pm UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/7 "2021-04-02T17:05:37Z")

</div>

Can you please share the working code with me which is fetching video from a url instead of webcam

---

<div class="post-metadata">

**Author:** ![Henrique\_Barone](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/henrique_barone/32/16129_2.png) [@Henrique\_Barone](https://discourse.threejs.org/u/Henrique_Barone)\
**Post date:** [April 22, 2021, 1:59am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/8 "2021-04-22T01:59:19Z")

</div>

Hey, sorry for delay. I Create an example based on seanwasere ([Profile - seanwasere - three.js forum](https://discourse.threejs.org/u/seanwasere)) code using THREE.VideoTexture  
Becouse security reason you should use this video in your server or localhost

[Download the video here](https://www.videvo.net/video/woman-playing-vr-on-green-screen/7819/)

```
<!DOCTYPE html>
<html>
<head>
<title>Three.js Chromakey shader</title>
<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r123/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js" integrity="sha512-LF8ZB1iTwi4Qvkm4pekHG4a437Y9Af5ZuwbnW4GTbAWQeR2E4KW8WF+xH8b9psevV7wIlDMx1MH9YfPqgKhA/Q==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/7/Stats.js" integrity="sha512-+nNqDCQd6FiN/mQKnpfI/UswnfuG/cjHZbv+amV/2PLiuGgN8+D5dKzR4PqnSY5pZBExGavZz+FdjIeI9WgpKQ==" crossorigin="anonymous"></script>

<style>
    body {
        overflow: hidden;
        margin: 0px;
    }
</style>
</head>

<body>

<video id="video" width="320" height="240" preload autoplay loop muted src="171003D_002_2K_preview.webm"></video>

<script id="vertexShader" type="glsl">

  varying vec2 vUv;
  void main( void ) {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
  }

</script>

<script id="fragmentShader" type="glsl">

  uniform vec3 keyColor;
  uniform float similarity;
  uniform float smoothness;
  varying vec2 vUv;
  uniform sampler2D map;
  void main() {

      vec4 videoColor = texture2D(map, vUv);

      float Y1 = 0.299 * keyColor.r + 0.587 * keyColor.g + 0.114 * keyColor.b;
      float Cr1 = keyColor.r - Y1;
      float Cb1 = keyColor.b - Y1;

      float Y2 = 0.299 * videoColor.r + 0.587 * videoColor.g + 0.114 * videoColor.b;
      float Cr2 = videoColor.r - Y2;
      float Cb2 = videoColor.b - Y2;

      float blend = smoothstep(similarity, similarity + smoothness, distance(vec2(Cr2, Cb2), vec2(Cr1, Cb1)));
      gl_FragColor = vec4(videoColor.rgb, videoColor.a * blend);
  }

</script>

<script>

let camera, controls, gridHelper, scene, renderer, stats;
let container, urlVideo, urlVideoTexture, chromakeyMaterial;

window.onload = init();

function init() {

    container = document.createElement( 'div' );
    document.body.appendChild( container );

    scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

    controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.minDistance = 5;
    controls.maxDistance = 20;
    controls.enableDamping = true;
    controls.dampingFactor = 0.5;

    gridHelper = new THREE.GridHelper(10, 10);

    gridHelper.position.y = -1.5;
    scene.add(gridHelper);

    camera.position.z = 5;

    // urlVideo

    urlVideo = document.getElementById("video");

    urlVideo.onplaying = function() {

      urlVideoTexture = new THREE.VideoTexture(urlVideo);
      urlVideoTexture.minFilter = THREE.LinearFilter;
      urlVideoTexture.magFilter = THREE.LinearFilter;

      const vertexShader = document.getElementById("vertexShader").textContent;
      const fragmentShader = document.getElementById("fragmentShader").textContent;

      // Cria o material usando a urlVideoTexture

      chromakeyMaterial = new THREE.ShaderMaterial({
          transparent: true,
          uniforms: {
              map: { value: urlVideoTexture },
              keyColor: { value: [0.0, 1.0, 0.0] },
              similarity: { value: 0.74 },
              smoothness: { value: 0.0 }
          },
          vertexShader: vertexShader,
          fragmentShader: fragmentShader
      });

      const geometry = new THREE.PlaneGeometry( 16, 9 );
      const mesh = new THREE.Mesh( geometry, chromakeyMaterial );
			geometry.scale( 0.4, 0.5, 0.5 );
      scene.add(mesh);

  }

    var data = {
        keyColor: [0, 255, 0],
        similarity: 0.74,
        smoothness: 0.0
    };

    var gui = new dat.GUI( { width: 300 } );
    gui.addColor(data, 'keyColor').onChange(() => updateKeyColor(data.keyColor));
    gui.add(data, 'similarity', 0.0, 1.0).onChange(() => updateSimilarity(data.similarity));
    gui.add(data, 'smoothness', 0.0, 1.0).onChange(() => updateSmoothness(data.smoothness));

    stats = new Stats();
    container.appendChild( stats.domElement );

    window.addEventListener('resize', onWindowResize, false);

}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    render();
}

function updateKeyColor(v) {
    chromakeyMaterial.uniforms.keyColor.value = [v[0] / 255, v[1] / 255, v[2] / 255];
}

function updateSimilarity(v) {
    chromakeyMaterial.uniforms.similarity.value = v;
}

function updateSmoothness(v) {
    chromakeyMaterial.uniforms.smoothness.value = v;
}

function animate() {
    requestAnimationFrame(animate);

    //if (urlVideo.readyState === urlVideo.HAVE_ENOUGH_DATA) {
    if (urlVideoTexture)
        urlVideoTexture.needsUpdate = true;
    //}
    controls.update();
    stats.update();
    render();

};

function render() {
    renderer.render(scene, camera);
}
animate();

```

---

<div class="post-metadata">

**Author:** ![rudrjain](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/rudrjain/32/17462_2.png) [@rudrjain](https://discourse.threejs.org/u/rudrjain)\
**Post date:** [April 26, 2021, 2:04pm UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/9 "2021-04-26T14:04:46Z")

</div>

Thanks for your help, I will test out the same

---

<div class="post-metadata">

**Author:** ![MrFurley](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mrfurley/32/32654_2.png) [@MrFurley](https://discourse.threejs.org/u/MrFurley)\
**Post date:** [January 19, 2023, 7:38am UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/10 "2023-01-19T07:38:53Z")

</div>

Thank you, @Mugen87! I reworked your shader code into a ChromaKeyMaterial module that can take a video or image texture.

[Github Repo](https://github.com/drinkspiller/threejs_chromakey_video_material)  
[Demo](https://drinkspiller.github.io/threejs_chromakey_video_material/)

---

<div class="post-metadata">

**Author:** ![POP](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pop/32/31182_2.png) [@POP](https://discourse.threejs.org/u/POP)\
**Post date:** [May 1, 2024, 12:50pm UTC](https://discourse.threejs.org/t/production-ready-green-screen-with-three-js/23113/13 "2024-05-01T12:50:50Z")

</div>

I’m unable to get your shader working on a green screen video.

 html, body{ background:#323232; outline: none; overflow: hidden; margin:0;}
