react-chromakeyed-image
React Component allowing color substitutions to be defined on an image, similar to how Chroma Key (or "Green Screen") works on video.
Installing
npm i react-chromakeyed-image
Using
Basic usage
In your React app:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed</h3> <ReactChromakeyedImage src="/static/240px-face.png" findColor="#fede58" replaceColor="#FF0000" />...
Notes:
- All instances of color
findColor
will be replaced withreplaceColor
. - Note that you can use
#rrggbb
,#rrggbbaa
,#rgb
or#rgba
forms of specifying a color. - If you omit the Alpha channel, it will be assumed to be
0xFF
.
Adding Tolerance
You've probably observed some "fringes" or artifacts in the above transformed image. Unless you have very tight control over your source images (e.g. they are machine-generated), you'll need to add the tolerance
prop, which specifies a plus-or-minus range to be applied to each r
, g
, b
, and a
value in the findColor
.
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Tolerant]</h3> <ReactChromakeyedImage src="/static/240px-face.png" findColor="#fede58" replaceColor="#FF0000" tolerance={10} />...
Using a color replacement map
If you need to transform more than one color, supply a colorReplacementMap
prop, using #rrggbb[aa]
-style colors as before:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Mapped]</h3> <ReactChromakeyedImage src="/static/240px-face.png" colorReplacementMap={{ "#fede58": "#00FF00", "#871945": "#00f"}} />...
Color replacement map with tolerance
To avoid the fringing effects visible in the above image, you can add the tolerance
prop when using a colorReplacementMap
too:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Mapped, Tolerant]</h3> <ReactChromakeyedImage src="/static/240px-face.png" colorReplacementMap={{ "#fede58": "#00FF00", "#871945": "#00f"}} tolerance={20} />...
Custom replacement function
Sometimes, what you need to do can't be expressed with a static map. For those times, you can supply a function as the replacementFunction
prop. The function takes 3 arguments, as per the following TypeScript declarations:
export type RGBAPixel = { r: number; b: number; g: number; a: number;} export type PixelReplacementFunction = (pixel: RGBAPixel, x:number, y:number) => RGBAPixel;
The first argument is the original {r, g, b, a}
value of the pixel. Then come the x
and y
co-ordinates of that pixel. The function should always return a pixel in the form {r, g, b, a}
, even if no change was made to it.
This allows you to apply different replacements depending on the co-ordinates within the image, as in the following example, which only makes changes to a small horizontal band of pixels, leaving all others unchanged:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Custom function]</h3> <ReactChromakeyedImage src="/static/240px-face.png" replacementFunction={ ( { r,g,b,a }, x, y ) => { if ( y > 50 && y < 120) { return { r: 0x30, g: 0x30, b: 0x30, a}; } return { r, g, b, a }; }} />...
Advanced usage
Custom blending modes
Simply replacing each pixel might be too crude - you may be looking for a more "layered" effect where the background image can still be made out through your foreground replacement, using Alpha transparency. In this case, supply a blendMode
prop with a value from the BlendMode
enumeration:
export enum BlendMode { OPAQUE_FOREGROUND, // This is the default ALPHA_BLENDING, ALPHA_RETAIN_BG_TRANSPARENCY};
Alpha Blending
Using the Alpha blending algorithm, the amount of a
returned in your map/function for a given pixel will control how much of the background shows through - 0xFF
would be fully opaque (like the default behaviour), 0x80
would be half-visible, etc:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Alpha Blending]</h3> <ReactChromakeyedImage blendMode={BlendMode.ALPHA_BLENDING} src="/static/240px-face.png" replacementFunction={({r,g,b,a},x, y) => { if ( y > 50 && y < 120) { return { r: 0x30, g: 0x30, b: 0x30, a }; } return { r, g, b, a}; }} />...
Alpha Blending with Retained Background Transparency
Alpha blending is good but if your source background image already has fully-transparent pixels (as in our sample face image), they are probably there for good reason. Note what happened to our image when we Alpha-blended it above; we lost the full transparency around the edges. In cases like these, use BlendMode.ALPHA_RETAIN_BG_TRANSPARENCY
- this checks to see if the Alpha channel of the background pixel is 0x00
(i.e. fully transparent) and if so, doesn't bother applying any transformation:
import ReactChromakeyedImage from 'react-chromakeyed-image'; ... <h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed [Alpha Blending (retained BG transparency)]</h3> <ReactChromakeyedImage blendMode={BlendMode.ALPHA_RETAIN_BG_TRANSPARENCY} src="/static/240px-face.png" replacementFunction={({r,g,b,a},x, y) => { if ( y > 50 && y < 120) { return { r: 0x30, g: 0x30, b: 0x30, a }; } return { r, g, b, a}; }} />...
Other features
Props are spread
Any props you give to ReactChromakeyedImage
will be spread onto the underlying HTML canvas
, so you can control the overall appearance of the image however you like; e.g.:
<h3>Original</h3> <img src="/static/240px-face.png" /> <h3>Chromakeyed (and styled)</h3> <ReactChromakeyedImage style={{width: '100px', height: '100px', border: '3px solid black', borderRadius: '8px' }} src="/static/240px-face.png" findColor="#fede58" replaceColor="#FF0000" />
RGBAPixel
s and color strings
Utility functions for working with Check out PixelUtils
and ColorStringUtils
for functions that might be useful when writing your own custom pixel replacement functions.