function tinycolor (color, opts) {
color = (color) ? color : '';
// If input is already a tinycolor, return itself
if (color instanceof tinycolor) {
// If we are called as a function, call using new instead
if (!(this instanceof tinycolor)) {
return new tinycolor(color, opts);
var rgb = inputToRGB(color);
this._originalInput = color,
this._roundA = mathRound(100*this._a) / 100,
this._format = opts.format || rgb.format;
this._gradientType = opts.gradientType;
// Don't let the range of [0,255] come back in [0,1].
// Potentially lose a little bit of precision here, but will fix issues where
// .5 gets interpreted as half of the total, instead of half of 1
// If it was supposed to be 128, this was already taken care of by `inputToRgb`
if (this._r < 1) { this._r = mathRound(this._r); }
if (this._g < 1) { this._g = mathRound(this._g); }
if (this._b < 1) { this._b = mathRound(this._b); }
this._tc_id = tinyCounter++;
return this.getBrightness() < 128;
getOriginalInput: function() {
return this._originalInput;
getBrightness: function() {
//http://www.w3.org/TR/AERT#color-contrast
return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
getLuminance: function() {
//http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
var RsRGB, GsRGB, BsRGB, R, G, B;
if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
setAlpha: function(value) {
this._a = boundAlpha(value);
this._roundA = mathRound(100*this._a) / 100;
var hsv = rgbToHsv(this._r, this._g, this._b);
return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
toHsvString: function() {
var hsv = rgbToHsv(this._r, this._g, this._b);
var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
"hsv(" + h + ", " + s + "%, " + v + "%)" :
"hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
var hsl = rgbToHsl(this._r, this._g, this._b);
return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
toHslString: function() {
var hsl = rgbToHsl(this._r, this._g, this._b);
var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
"hsl(" + h + ", " + s + "%, " + l + "%)" :
"hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
toHex: function(allow3Char) {
return rgbToHex(this._r, this._g, this._b, allow3Char);
toHexString: function(allow3Char) {
return '#' + this.toHex(allow3Char);
toHex8: function(allow4Char) {
return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char);
toHex8String: function(allow4Char) {
return '#' + this.toHex8(allow4Char);
return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
toRgbString: function() {
"rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
"rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
toPercentageRgb: function() {
return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
toPercentageRgbString: function() {
"rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
"rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
toFilter: function(secondColor) {
var hex8String = '#' + rgbaToArgbHex(this._r, this._g, this._b, this._a);
var secondHex8String = hex8String;
var gradientType = this._gradientType ? "GradientType = 1, " : "";
var s = tinycolor(secondColor);
secondHex8String = '#' + rgbaToArgbHex(s._r, s._g, s._b, s._a);
return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
toString: function(format) {
var formatSet = !!format;
format = format || this._format;
var formattedString = false;
var hasAlpha = this._a < 1 && this._a >= 0;
var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name");
// Special case for "transparent", all other non-alpha formats
// will return rgba when there is transparency.
if (format === "name" && this._a === 0) {
return this.toRgbString();
formattedString = this.toRgbString();
formattedString = this.toPercentageRgbString();
if (format === "hex" || format === "hex6") {
formattedString = this.toHexString();
formattedString = this.toHexString(true);
formattedString = this.toHex8String(true);
formattedString = this.toHex8String();
formattedString = this.toName();
formattedString = this.toHslString();
formattedString = this.toHsvString();
return formattedString || this.toHexString();
return tinycolor(this.toString());
_applyModification: function(fn, args) {
var color = fn.apply(null, [this].concat([].slice.call(args)));
return this._applyModification(lighten, arguments);
return this._applyModification(brighten, arguments);
return this._applyModification(darken, arguments);
return this._applyModification(desaturate, arguments);
return this._applyModification(saturate, arguments);
return this._applyModification(greyscale, arguments);
return this._applyModification(spin, arguments);
_applyCombination: function(fn, args) {
return fn.apply(null, [this].concat([].slice.call(args)));
return this._applyCombination(analogous, arguments);
return this._applyCombination(complement, arguments);
monochromatic: function() {
return this._applyCombination(monochromatic, arguments);
splitcomplement: function() {
return this._applyCombination(splitcomplement, arguments);
return this._applyCombination(triad, arguments);
return this._applyCombination(tetrad, arguments);
// If input is an object, force 1 into "1.0" to handle ratios properly
// String input requires "1.0" as input, so 1 will be treated as 1
tinycolor.fromRatio = function(color, opts) {
if (typeof color == "object") {
if (color.hasOwnProperty(i)) {
newColor[i] = convertToPercentage(color[i]);
return tinycolor(color, opts);
// Given a string or object, convert that input to RGB
// Possible string inputs:
// "#ff000000" or "ff000000"
// "rgb 255 0 0" or "rgb (255, 0, 0)"
// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
function inputToRGB(color) {
var rgb = { r: 0, g: 0, b: 0 };
if (typeof color == "string") {
color = stringInputToObject(color);
if (typeof color == "object") {
if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
rgb = rgbToRgb(color.r, color.g, color.b);
format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
s = convertToPercentage(color.s);
v = convertToPercentage(color.v);
rgb = hsvToRgb(color.h, s, v);
else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
s = convertToPercentage(color.s);
l = convertToPercentage(color.l);
rgb = hslToRgb(color.h, s, l);
if (color.hasOwnProperty("a")) {
format: color.format || format,
r: mathMin(255, mathMax(rgb.r, 0)),
g: mathMin(255, mathMax(rgb.g, 0)),
b: mathMin(255, mathMax(rgb.b, 0)),
// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
// Handle bounds / percentage checking to conform to CSS color spec
// <http://www.w3.org/TR/css3-color/>
// *Assumes:* r, g, b in [0, 255] or [0, 1]
// *Returns:* { r, g, b } in [0, 255]
function rgbToRgb(r, g, b){
r: bound01(r, 255) * 255,
g: bound01(g, 255) * 255,
// Converts an RGB color value to HSL.
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
// *Returns:* { h, s, l } in [0,1]
function rgbToHsl(r, g, b) {
var max = mathMax(r, g, b), min = mathMin(r, g, b);
var h, s, l = (max + min) / 2;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
return { h: h, s: s, l: l };
// Converts an HSL color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
function hslToRgb(h, s, l) {
function hue2rgb(p, q, t) {
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
r = g = b = l; // achromatic
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
r = hue2rgb(p, q, h + 1/3);
b = hue2rgb(p, q, h - 1/3);
return { r: r * 255, g: g * 255, b: b * 255 };
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
function rgbToHsv(r, g, b) {
var max = mathMax(r, g, b), min = mathMin(r, g, b);
s = max === 0 ? 0 : d / max;
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
return { h: h, s: s, v: v };
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
function hsvToRgb(h, s, v) {
t = v * (1 - (1 - f) * s),
r = [v, q, p, p, t, v][mod],
g = [t, v, v, q, p, p][mod],
b = [p, p, t, v, v, q][mod];
return { r: r * 255, g: g * 255, b: b * 255 };
// Converts an RGB color to hex
// Assumes r, g, and b are contained in the set [0, 255]
// Returns a 3 or 6 character hex
function rgbToHex(r, g, b, allow3Char) {
pad2(mathRound(r).toString(16)),
pad2(mathRound(g).toString(16)),
pad2(mathRound(b).toString(16))
// Return a 3 character hex if possible
if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);