abachrome

How do I generate a color palette with smooth interpolation in Ruby?

Generating a palette by hand — picking each color individually — is tedious and often produces inconsistent results. Interpolating between a small set of anchor colors is faster and tends to produce more harmonious output, particularly when the interpolation happens in a perceptually uniform space. We can use Abachrome::Palette for exactly this: it holds an ordered list of colors and generates interpolated steps between them. Because blending happens in OKLCH by default, every intermediate color has consistent perceived lightness and saturation.

Basic Palette Generation

require 'abachrome'

stops = [
  Abachrome.from_oklch(0.4, 0.15, 260),  # dark blue
  Abachrome.from_oklch(0.7, 0.18, 145),  # mid green
  Abachrome.from_oklch(0.9, 0.12, 80),   # light yellow
]

palette = Abachrome::Palette.new(stops)
steps   = palette.interpolate(steps: 11)

steps.each do |color|
  puts Abachrome::Outputs::CSS.format(color.to_srgb)
end

The steps parameter controls how many colors you get back in total, including the original anchor colors. A higher number produces a finer gradient.

Outputting as CSS Custom Properties

palette = Abachrome::Palette.new([
  Abachrome.from_hex('#264653'),
  Abachrome.from_hex('#2a9d8f'),
  Abachrome.from_hex('#e9c46a'),
  Abachrome.from_hex('#f4a261'),
  Abachrome.from_hex('#e76f51'),
])

ramp = palette.interpolate(steps: 9)

css = ramp.each_with_index.map do |color, i|
  "  --color-#{i + 1}: #{Abachrome::Outputs::CSS.format(color.to_srgb)};"
end.join("\n")

puts ":root {\n#{css}\n}"

This pattern is useful when building a design token system — you define your anchor colors, interpolate to fill the gaps, and emit them as CSS custom properties that the rest of your stylesheet can reference.

Diverging Scales for Data Visualization

A two-stop diverging palette (negative → neutral → positive) works well for heatmaps and choropleth maps:

negative = Abachrome.from_oklch(0.55, 0.22, 20)   # warm red
neutral  = Abachrome.from_oklch(0.92, 0.01, 0)    # near-white
positive = Abachrome.from_oklch(0.55, 0.20, 250)  # cool blue

scale = Abachrome::Palette.new([negative, neutral, positive])
        .interpolate(steps: 11)

Keeping the lightness values similar across negative and positive anchors ensures neither end of the scale appears visually dominant — an important consideration for accessible data visualization.

Notes

  • The steps count includes the original anchor colors; pass a higher number for finer gradients.
  • Always call .to_srgb before formatting for CSS output — OKLCH coordinates are not valid in standard CSS without the oklch() function.