Reputation: 5742
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
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")
Upvotes: 8
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
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")
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:
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
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
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)
Upvotes: 15
Reputation: 145745
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)
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
Reputation: 456
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