Skip to content

Instantly share code, notes, and snippets.

@KyleSmith0905
Last active December 20, 2021 01:10
Show Gist options
  • Save KyleSmith0905/3df5f323b966a535bba531536fa6182d to your computer and use it in GitHub Desktop.
Save KyleSmith0905/3df5f323b966a535bba531536fa6182d to your computer and use it in GitHub Desktop.
Sine HSV to RGB

Sine HSV to RGB

An HSV to RGB converter built for rainbow gradients.


Why Sine HSV?

With regular HSV, hue changes one value at a time. For example, as hue goes from 0° (red) to 60° (yellow) (with value and saturation unchanged), there are twice as many pixels lit. Think about that, only because a color is a different hue, it's twice as bright.

Using /visualization.py, here is a comparison of HSV.

HSV converted to RGB using a standard library:

Normal HSV

HSV converted to RGB using Sine HSV

Sine HSV

Notice how regular HSV have a few extreme values, whereas Sine HSV looks like a long gradient.

Normal HSV under a grayscale filter.

Normal HSV Grayscale

Sine HSV under a grayscale filter.

Sine HSV Grayscale

The grayscale images show what's happening with normal HSV. All 3 local maximum brightnesses are the secondary colors. The brightness is somewhat attributed the distance the hue is from the secondary colors. Let's look at Sine HSV grayscaled, notice there are only 2 local extremes. It's lighter in an area simply because green happens to be lighter than red and blue.


Flaws

  • With Sine HSV, there is no feasible way to get every possible color. Although probably easily fixable, the closest red is #ff4040 instead of #ff0000. Although not the purpose to obtain every color, it's entirely fixable and my be implemented in the future.
  • Sine HSV has less color even with max saturation and value. This is related to another flaw on the list, also fixable. Here is a Desmos graph to help understand why there might be gray: https://www.desmos.com/calculator/psiqyau9zn.

How Does It Work?

const SineHSVToRGB = ({hue, saturation, value}) => {
	// Prevents saturation and value from going beyond 0-100. An error could also be thrown.
	if (saturation < 0) saturation = 0;
	else if (saturation > 100) saturation = 100;
	if (value < 0) value = 0;
	else if (value > 100) value = 100;

	// Transform 0-360 hue to pi-3*pi.
	const hueTransform = (-Math.PI * (hue % 360)) / 180 + Math.PI;
	// Transform 0-100 saturation to 0-128
	const saturationScalar = saturation * (128/100);
	// Transform 0-100 saturation to 256-0
	const saturationTransform = saturation * (-128 / 100) + 256;
	// Transform 0-100 value to 0-1
	const valueScalar = value / 100;

	const HSVtoElement = (offset: number): number => {
		let element = Math.floor((Math.sin(hueTransform + (Math.PI * offset)) * saturationScalar + saturationTransform) * valueScalar);
		// Deals with rare cases where the color is beyond pixel values.
		if (element > 255) element = 255;
		else if (element < 0) element = 0;
		return element;
	}
	
	// Transforms HSV to R, G, and B seperately by a horizontal transformation of the sine function.
	const red = HSVtoElement(3/2);
	const green = HSVtoElement(1/6);
	const blue = HSVtoElement(5/6);


	return {red, green, blue};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment