Color equivalency generator for Figma

This snippet applies the logic found in the color equivalency script to generate color ramps that are optically indistinguishable, but have varying opacities. Ramps like this can be especially helpful within design systems that are looking to have semi-transparent colors that remain optically equivalent to an existing color palette.

Instructions

This script is intended to run within Figma through its plugin API. The fastest way to run this script is by copy-pasting within Scripter.

            

const BACKGROUND_COLOR = '#FFFFFF';
const BASE_COLOR = "#111111"
const FRAME_NAME = 'Color equivalency for ' + BASE_COLOR;
const PADDING = 20;
const GAP = 10
const FILL_SEED = [{
	type: "SOLID",
	visible: true,
	opacity: 1,
	blendMode: "NORMAL",
	color: {
		r: 1,
		g: 1,
		b: 1
	},
	boundVariables: {}
}]

const SWATCH_WIDTH = 100;
const SWATCH_HEIGHT = 20;

const ALPHAS = [
	1, .9, .8, .7, .6, .5, .4, .3, .2, .1
]

function matchColorAtAlpha(background, base, alpha):Object {
	let r = ((base.r - background.r)*alpha + background.r)
	let g = ((base.g - background.g)*alpha + background.g)
	let b = ((base.b - background.b)*alpha + background.b)

	return {r:r, g:g, b:b};
}

function getAlphaToMatchColorOnBackground(background, baseColor, newColor):Object {
	let alphaR = (baseColor.r-background.r) / (newColor.r-background.r)
	let alphaG = (baseColor.g-background.g) / (newColor.g-background.b)
	let alphaB = (baseColor.b-background.b) / (newColor.b-background.g)	

	return (alphaR + alphaG + alphaB)/3;
}

function hexToRgb(hex):Object {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    r: parseInt(result[1], 16)/255,
    g: parseInt(result[2], 16)/255,
    b: parseInt(result[3], 16)/255
  } as Object : {};
}

let backgroundRGB = hexToRgb(BACKGROUND_COLOR);
let baseColorRGB = hexToRgb(BASE_COLOR);
let equivRGB;

let frame:FrameNode = figma.createFrame();
frame.name = FRAME_NAME;

var frameFill = FILL_SEED;
let backgroundFrameColorRGB = hexToRgb(BACKGROUND_COLOR);
frameFill[0].color.r = backgroundFrameColorRGB.r
frameFill[0].color.g = backgroundFrameColorRGB.g
frameFill[0].color.b = backgroundFrameColorRGB.b

frame.fills = frameFill;

frame.layoutMode = 'HORIZONTAL';
frame.paddingLeft = PADDING;
frame.paddingRight = PADDING;
frame.paddingTop = PADDING;
frame.paddingBottom = PADDING;
frame.itemSpacing = GAP;
frame.layoutSizingHorizontal = 'HUG';
frame.layoutSizingVertical = 'HUG';

let alphaFrame:FrameNode = figma.createFrame();
alphaFrame.layoutMode = 'VERTICAL';
alphaFrame.layoutSizingHorizontal = 'HUG';
alphaFrame.layoutSizingVertical = 'HUG';
alphaFrame.fills = []

frame.appendChild(alphaFrame)

let equivFrame:FrameNode = figma.createFrame();
equivFrame.layoutMode = 'VERTICAL';
equivFrame.layoutSizingHorizontal = 'HUG';
equivFrame.layoutSizingVertical = 'HUG';
equivFrame.fills = []

frame.appendChild(equivFrame)

let alphaRect:RectangleNode;
let equivRect:RectangleNode;
let alphaFill:Paint;
let equivFill:Paint;

let equivRGBColors = []

for(let i = 0; i < ALPHAS.length; i++) {
	alphaRect = figma.createRectangle();
	alphaRect.resize(SWATCH_WIDTH, SWATCH_HEIGHT);

	alphaFill = FILL_SEED;
	alphaFill[0].color.r = baseColorRGB.r;
	alphaFill[0].color.g = baseColorRGB.g;
	alphaFill[0].color.b = baseColorRGB.b;
	alphaFill[0].opacity = ALPHAS[i];

	alphaRect.fills = alphaFill;
	alphaFrame.appendChild(alphaRect)

	equivRect = figma.createRectangle();
	equivRect.resize(SWATCH_WIDTH, SWATCH_HEIGHT);

	equivRGB = matchColorAtAlpha(backgroundRGB, baseColorRGB, ALPHAS[i]);
	equivRGBColors.push(equivRGB)

	equivFill = FILL_SEED;
	equivFill[0].color.r = equivRGB.r;
	equivFill[0].color.g = equivRGB.g;
	equivFill[0].color.b = equivRGB.b;
	equivFill[0].opacity = 1;

	equivRect.fills = equivFill;
	equivFrame.appendChild(equivRect)

}

let equivAlphaFrame:FrameNode = figma.createFrame();
equivAlphaFrame.layoutMode = 'VERTICAL';
equivAlphaFrame.layoutSizingHorizontal = 'HUG';
equivAlphaFrame.layoutSizingVertical = 'HUG';
equivAlphaFrame.fills = []
frame.appendChild(equivAlphaFrame)

for(let i = 0; i < equivRGBColors.length; i++) {

	let equivAlphaColors:FrameNode = figma.createFrame();
	equivAlphaColors.layoutMode = 'HORIZONTAL';
	
	equivAlphaColors.resize(SWATCH_HEIGHT, SWATCH_HEIGHT)
	equivAlphaColors.layoutSizingHorizontal = 'HUG';
	equivAlphaColors.fills = []
	equivAlphaFrame.appendChild(equivAlphaColors)
	for(let j = 0; j < i+1; j++) {

		let swatch = figma.createRectangle();
		swatch.resize(SWATCH_HEIGHT, SWATCH_HEIGHT);
		let equivAlpha = getAlphaToMatchColorOnBackground(backgroundRGB, equivRGBColors[i], equivRGBColors[j])
		
		equivFill = FILL_SEED;
		equivFill[0].color.r = equivRGBColors[j].r;
		equivFill[0].color.g = equivRGBColors[j].g;
		equivFill[0].color.b = equivRGBColors[j].b;
		equivFill[0].opacity = equivAlpha;

		swatch.fills = equivFill;
		equivAlphaColors.appendChild(swatch);
	}
}