Alby
Alby

Reputation: 5742

Is there a way to programmatically darken the color given RGB values?

Let's say I have the RGB values like this (in R, for example):

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

Is there any way to programmatically derive another set of colors which is a darkened version of the former?

It doesn't have to be R.

Upvotes: 21

Views: 9025

Answers (7)

RoB
RoB

Reputation: 1984

You can do this very easily in base graphics using colorRampPalette().

Just make a gradient with your initial color and black, ramp 100 and select "how much darker" you want it (1=initial and 100=black) :

colorRampPalette(c(color, "black"))(100)[how.much]

Change "black" to "white" for making the color lighter (as pointed out in the comments). You can make this into neat little functions, see below.

For completeness' sake, I'll also throw in a function to make a color more transparent, which is a bit less obvious.

# Functions
darker.col <- function(color, how.much = 30){
  colorRampPalette(c(color, "black"))(100)[how.much]
}

lighter.col <- function(color, how.much = 30){
  colorRampPalette(c(color, "white"))(100)[how.much]
}

transparent.col <-function(color, how.much = 30)
{
  unlist(lapply(color, function(c){
                   c <-  grDevices::col2rgb(c)
                   return(grDevices::rgb(
                       r=c[1], g=c[2], b=c[3], 
                       alpha=255-(how.much/100)*255, 
                       maxColorValue=255))
                   }))
}

# Plotting
how.much <- 1:100
y <- cos(1:100/10) # to spread out the dots better

par(mfrow=c(3,1))
plot(how.much,y, pch=16, cex=5, col=darker.col("royalblue", 1:100), main="darker.col")
plot(how.much,y, pch=16, cex=5, col=lighter.col("royalblue", 1:100), main = "lighter.col")
plot(how.much,y, pch=16, cex=5, col=transparent.col("royalblue", 1:100), main = "transparent.col")

plots

Upvotes: 8

Justin
Justin

Reputation: 41

Here's something simple that works for me and returns the colors in the same format as the original:

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")    
rgb(hex2RGB(cols)@coords * 0.8)

Upvotes: 0

Claus Wilke
Claus Wilke

Reputation: 17790

The question asks whether there is any way to programmatically darken colors. The problem is that there are many different ways, and they all yield different results. The specific outcome you obtain depends on the specific algorithm employed and the color space used.

The R package colorspace now provides a built-in function to darken colors, through the darken() function. This function uses a new "combined" colorspace we came up with which is a mix between HLS and HCL. (In brief, adjusts L in HCL space but adjusts C by taking a detour through HLS, while keeping H constant.)

To use this function, you need to install the current development version of colorspace:

install.packages("colorspace", repos = "http://R-Forge.R-project.org")

Then, try the following:

# original colors
cols <- c("#CDE4F3", "#E7F3D3", "#F7F0C7", "#EFCFE5", "#D0D1E7")

# darken 20%
cols_d2 <- darken(cols, 0.2)

# darken 40%
cols_d4 <- darken(cols, 0.4)

# plot
pal <- function(col, border = "light gray") {
  n <- length(col)
  plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1), axes = FALSE,
       xlab = "", ylab = "")
  rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border)
}

par(mfrow = c(3, 1), mar = c(1, 0, 2, 0))
pal(cols); mtext("original")
pal(cols_d2); mtext("20% darker")
pal(cols_d4); mtext("40% darker")

enter image description here

There are a couple of different color spaces and other adjustment options you can try, but the default should work in most cases.

To see the effect of darkening in different color spaces, consider what happens when we darken the same colors in HCL or HLS:

enter image description here

The HCL-darkened colors seem quite gray, and the HLS-darkened colors appear overly bright and colorful. However, depending on your specific application, you might want one of these outcomes.

Upvotes: 23

Dieter Menne
Dieter Menne

Reputation: 10205

It's not really nice code. The munsell package might be more friendly

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

lab = as(hex2RGB(cols),"LAB")
lab@coords[,1] = lab@coords[,1] *0.3 # 
cols1 =  hex(as(lab,"RGB"))
cols1

Upvotes: 4

Roland
Roland

Reputation: 132576

library(colorspace)

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")

cols1 <- readhex(file = textConnection(paste(cols, collapse = "\n")),
                 class = "RGB")
#transform to hue/lightness/saturation colorspace
cols1 <- as(cols1, "HLS")
cols2 <- cols1
#additive decrease of lightness
cols1@coords[, "L"] <- pmax(0, cols1@coords[, "L"] - 0.3)
#multiplicative decrease of lightness
cols2@coords[, "L"] <- cols2@coords[, "L"] * 0.75
#going via rgb seems to work better  
cols1 <- as(cols1, "RGB")
cols1 <- hex(cols1)
cols2 <- as(cols2, "RGB")
cols2 <- hex(cols2)


plot(x = seq_along(cols), y = rep(1, length(cols)), 
     col = cols, pch = 15, ylim = c(0, 4.5), cex = 5,
     xlab = "", ylab = "")
points(x = seq_along(cols), y = rep(2, length(cols)), 
       col = cols1, pch = 16, cex = 5)
points(x = seq_along(cols), y = rep(3, length(cols)), 
       col = cols2, pch = 17, cex = 5)

legend("top",legend = c("original", "additive", "multipl."), 
       pch = 15:17, ncol = 3)

resulting plot

Upvotes: 15

Gregor Thomas
Gregor Thomas

Reputation: 145745

HSV Value Adjustment

This seems much better than my first stab with Munsell colors (below). It's still a bit of a run-around (probably because I'm mixing packages that specify colors in rows and columns and take matrices or not), but it works:

cols.hsv = rgb2hsv(cols.rgb)
# adjust the "value" down to 80% of it's previous level
cols.hsv["v", ] = cols.hsv["v", ] * 0.8

cols.darker = cols
for (i in seq_along(cols)) {
    cols.darker[i] = hsv(cols.hsv[1, i], cols.hsv[2, i], cols.hsv[3, i])
}

par(mfrow = c(1, 2))
scales::show_col(cols)
scales::show_col(cols.darker)

Munsell

I've not used the munsell package before, so I might be making this more complicated than it needs to be, but it has a function darker that "Decreases the value of the Munsell colour by 1."

The hard part is conversions. As near as I can tell, we need to get your hex colors to Munsell colors we have to go via RGB.

cols <- c("#CDE4F3","#E7F3D3","#F7F0C7","#EFCFE5","#D0D1E7")
cols.rgb = col2rgb(cols)

library(munsell)
# munsell expects rgb colors in rows, not columns, and expects the
# values to be between 0 and 1, not 0 and 255
cols.m = rgb2mnsl(t(cols.rgb) / rowSums(t(cols.rgb)))

# make darker
darker.m = darker(cols.m)
# at least converting back to hex is one step!
darker.hex = mnsl2hex(darker.m)


# view the results
par(mfrow = c(2, 1))
scales::show_col(cols)
scales::show_col(darker.hex)

Overall I'm not thrilled with this solution. It made the colors much darker and I don't see a way to adjust that in the darker function.

Upvotes: 6

Rene Ramirez
Rene Ramirez

Reputation: 456

Yes there is.

You will have to specify how darken or lighter you what to go.

Here is a Function done in JavaScript: https://css-tricks.com/snippets/javascript/lighten-darken-color/

function LightenDarkenColor(col, amt) {
    var usePound = false;
    if (col[0] == "#") {
        col = col.slice(1);
        usePound = true;
    }
    var num = parseInt(col,16);
    var r = (num >> 16) + amt;
    if (r > 255) {
      r = 255;
    }else if  (r < 0){ 
      r = 0;
    }
    var b = ((num >> 8) & 0x00FF) + amt;
    if (b > 255) {
      b = 255;
    }else if  (b < 0) {
      b = 0;
    }
    var g = (num & 0x0000FF) + amt;
    if (g > 255) {
      g = 255;
    }else if (g < 0) {
      g = 0;
    }
    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}

Hope it helps, Cheers.

Upvotes: 5

Related Questions