I manually extract RGB colors for 225 websites and graph them: red > blue > green > purple > yellow-brown.
To extend Gwern.net link-icons to include site-specific color when interacting with them, I collected 225 RGB hex colors from websites or their logos.
I visualize the color distributions in R in several ways: by channel, pairs of channels, color wheel, and 3D scatterplots.
The collected colors show a large polarization of website design towards either red or blue as the accent color, but little yellow-brown.
This shows the interesting primacy of red vs blue, but also suggests that maybe they have become overused, and people should explore the other colors, like yellow-brown.
Using Hacker news one day in October 2024, I saw a blog which made a point of coloring its link to its Twitter account in Twitter-blue, but on hover, to avoid distracting the reader but also confirm that you are interacting with what you think you are. I immediately thought this would be a great feature for Gwern.net, as a way to extend the existing link-icons by bring in the additional metadata of brand/logo/icon coloring, but without breaking the overall monochrome theme. Coloring on hover seemed to fit in perfectly with the overall design philosophy of semantic enrichment: it only provides the metadata when you are interacting with that particular link, and otherwise is invisible.
Link-Icon Colors
Said Achmiz decided to give it a try, and after some experimenting, he found that it was possible to color the existing SVGs/text automatically with our link-icon infrastructure, which looked OK… but not great; various kinds of outlining or drop-shadowing failed badly too; then we tried coloring the link underlines, and that looked good. (In keeping with Saul Bass’s discussion of stripes: stripes just look fast, modern, and cool, so it’s hard to go too wrong with stripes.) The Twitter links looked especially good in dark-mode with the blue-colored underlining & bird icon (early sample). So we went forward with it site-wide to experiment with it at scale.
One of the first big obstacles was that the existing link-icon configuration had no idea what colors were relevant; we had carefully removed all color from even the SVG logos. So, I spent a day going through the corpus of existing link-icons, browsing websites to look at their logos or CSS, and use a web browser’s dev tool’s color picker to get the exact RGB color value.1
While doing this, I noticed that some colors were much more popular than others. Red, for example, was heavily used (which I already knew, see rubrication), and so I guess the secret is out about the efficacy of red in web design, but I was a little surprised to realize that blue was so heavily used (I had mostly associated it with Google/Facebook)—but also orange is surprisingly common beyond just “the orange site”…? Meanwhile, purple appears to be increasingly popular, particularly for sites which are adopting a dark esthetic. I suppose if you default to black backgrounds, you don’t have to provide a separate dark mode! Green was perhaps somewhat underrepresented. But the absence of any yellow or brown was striking. This makes me wonder if perhaps designers should consider trying to mine the yellow-brown space more, rather than continue herding into red/blue?
After I finished up, I decided to investigate a little more rigorously, and pulled out all the raw RGB codes, before we did any kind of perceptual adjustment to make them nice link-colors.2 This resulted in 225 unique color uses.
Because I have to define link-icon rules & test-cases for sub-domains or across multiple websites or file formats, this doesn’t equate to “225 websites”, but it’s similar, and large enough to show patterns in the sites that I link to.3
Visualizing The Colors
Courtesy of Claude-3 (since I don’t care enough about this topic enough to hack & slash through the pain & suffering of data visualizations in R), I plotted color patterns in a few ways:
## Read the CSV data
rgb <- read.csv("https://gwern.net/doc/cs/css/2024-11-08-gwern-gwernnetlinkicons-colorbysite.csv",
colClasses=c("character", "character"))
## convert RGB,URL → to RGB frequencies:
color_counts <- table(rgb$RGB)
colors_df <- data.frame(
RGB = names(color_counts),
N = as.numeric(color_counts))
## Convert hex to RGB components
library(tidyverse)
colors_df <- colors_df %>%
mutate(
r = strtoi(substring(RGB, 2, 3), base=16),
g = strtoi(substring(RGB, 4, 5), base=16),
b = strtoi(substring(RGB, 6, 7), base=16))
Visualizing data:
library(skimr)
skim(colors_df)
# ── Data Summary ────────────────────────
# Values
# Name colors_df
# Number of rows 225
# Number of columns 5
# _______________________
# Column type frequency:
# character 1
# numeric 4
# ________________________
# Group variables None
#
# ── Variable type: character ────────────────────────────────────────────────────
# skim_variable n_missing complete_rate min max empty n_unique whitespace
# 1 RGB 0 1 7 7 0 225 0
#
# ── Variable type: numeric ──────────────────────────────────────────────────────
# skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
# 1 N 0 1 1.62 1.69 1 1 1 2 20 ▇▁▁▁▁
# 2 r 0 1 122. 95.9 0 29 107 221 255 ▇▂▂▃▆
# 3 g 0 1 98.4 65.4 0 42 94 147 255 ▇▇▆▃▂
# 4 b 0 1 101. 82.2 0 25 81 177 255 ▇▃▃▃▃
Small multiples for the 3 color channels, univariate histogram:
library(ggplot2)
library(patchwork)
p1 <- ggplot(colors_df, aes(x = r, weight = N)) +
geom_histogram(fill = "red") +
labs(title = "Red Channel Distribution") +
theme_bw(base_size=40)
p2 <- ggplot(colors_df, aes(x = g, weight = N)) +
geom_histogram(fill = "green") +
labs(title = "Green") +
theme_bw(base_size=40)
p3 <- ggplot(colors_df, aes(x = b, weight = N)) +
geom_histogram(fill = "blue") +
labs(title = "Blue") +
theme_bw(base_size=40)
p1 / p2 / p3
Graphing the Red vs Green vs Blue color distributions against each other:
## Graph R vs G vs B separately
## Create 3 different 2D projections
plot_rg <- ggplot(colors_df, aes(x=r, y=g, size=N, color=RGB)) +
geom_point(alpha=0.9) +
scale_color_identity() +
scale_size_continuous(range = c(8, 20)) +
scale_x_continuous(limits = c(0, 255)) +
scale_y_continuous(limits = c(0, 255)) +
labs(
title = "Red vs Green",
x = "Red",
y = "Green",
size = "Frequency") +
theme_bw(base_size=40)
plot_rb <- ggplot(colors_df, aes(x=r, y=b, size=N, color=RGB)) +
geom_point(alpha=0.9) +
scale_color_identity() +
scale_size_continuous(range = c(8, 20)) +
scale_x_continuous(limits = c(0, 255)) +
scale_y_continuous(limits = c(0, 255)) +
labs(
title = "Red vs Blue",
x = "Red",
y = "Blue",
size = "Frequency") +
theme_bw(base_size=40)
plot_gb <- ggplot(colors_df, aes(x=g, y=b, size=N, color=RGB)) +
geom_point(alpha=0.9) +
scale_color_identity() +
scale_size_continuous(range = c(8, 20)) +
scale_x_continuous(limits = c(0, 255)) +
scale_y_continuous(limits = c(0, 255)) +
labs(
title = "Green vs Blue",
x = "Green",
y = "Blue",
size = "Frequency") +
theme_bw(base_size=40)
library(gridExtra)
grid.arrange(plot_rg, plot_rb, plot_gb, ncol=1) # 3×1
Color wheel representation:
## ensure RGB values are in 0--1 scale:
colors_df <- colors_df %>%
mutate(
r = r/255,
g = g/255,
b = b/255)
## Create matrix for `rgb2hsv` (note the different matrix structure)
rgb_matrix <- matrix(c(colors_df$r, colors_df$g, colors_df$b), ncol=3, byrow=FALSE)
hsv_matrix <- rgb2hsv(t(rgb_matrix))
## Now add HSV columns
colors_df <- colors_df %>%
mutate(
hue = hsv_matrix[1,],
saturation = hsv_matrix[2,],
value = hsv_matrix[3,])
## Plot on polar coordinates
library(ggplot2)
ggplot(colors_df, aes(x = hue * 360, y = saturation, size = N, color = RGB)) +
geom_point(alpha=0.9) +
scale_size_continuous(range = c(8, 20)) +
coord_polar() +
scale_color_identity() +
theme_bw(base_size=50) +
labs(x = "Hue°")
3D volume scatterplot, using rgl for interactive 3D visualization:
library(rgl)
par3d(family = "sans", cex = 4) # quadruple text size as unreadably small
plot3d(colors_df$r, colors_df$g, colors_df$b,
col = colors_df$RGB,
size = colors_df$N/2,
xlab = "Red", ylab = "Green", zlab = "Blue",
type = "s", # spheres
alpha = 0.7)
Underused: Yellow?
Overall, the visualizations line up with my subjective impressions: red > blue > green > purple > yellow-brown.
It may be worthwhile exploring more purple or yellow-heavy designs. While purple remains a tricky color to work with, due to its extremely strong connotations of royalty, gay men, or small girls (eg. My Little Pony), or all 3 (Catholicism), yellow-brown seems more versatile. Possibly yellow has been underused for a long time because computer monitors were not up to subtler colors and we suffered under the tyranny of “web-safe colors” (oh no, not the bees! ahh!), but now that bright high-resolution displays are common on both desktop & smartphones (sometimes with larger colorspaces) and we have many fancy technologies like textures and vector graphics, a yellow palette may be more viable.
-
I recommend using Firefox for this, because the Chromium color picker has strange UX limitations, like not being usable at all on websites with table-based layouts—it just doesn’t show up anywhere in the UI, while the Firefox dev tools color picker is always available. Afterwards, I learned that my MATE GUI desktop environment provides
mate-color-select
which seems like it’d work as well. Mac OS users can use “Digital Color Meter” (default install).↩︎ -
Due to the thinness of the link underlines & smallness of the link icons, a pale or dark color, which was clear when used in a large logo or in large site design elements such as buttons, may vanish or just look like black, so they need lightness adjustments, perhaps using a better colorspace than RGB like LAB. Other challenges include the need for alternate SVG icons for cases where a single color is inadequate (what color is Microsoft?) and supporting the common case of “white-letter-on-colored-background”, which would be unreasonably burdensome to solve by manually creating SVGs one by one.↩︎
-
Obviously, I am making no claims about the Internet in general—the color trends could be entirely different elsewhere on the Anglophone Internet, never mind the Chinese Internet etc. I am simply interested in the distribution of colors across the websites I tend to read or link to.↩︎