# Generating human-friendly color schemes

Date: 2020-09-19 18:43:07 +08:00Date modified: 2021-05-04 20:52:07 +08:00

Though we already have Human-friendly colorspaces, it doesn't mean that we can simply generate human-friendly color schemes. Certain colors can be perceived as too bright or too dark and having them grouped in a color palette will look out-of-place and/or horrible.

Color spaces such as HSLuv and CIELUV respects perceptual uniformity which approximates how human vision see things. We can simply generate colors with the aforementioned color spaces. Here's an example of a Base16 color scheme generation from terminal.sexy ported to Rust.

    use std::error::Error;
    use std::collections::HashMap;
    use std::env;

    use hsluv;
    use rand_chacha::ChaCha8Rng;
    use rand::{Rng, SeedableRng};

    #[macro_use]
    use serde::{Deserialize, Serialize};

    // Based from the implemention at terminal.sexy repo
    // at https://github.com/stayradiated/terminal.sexy/blob/2e2c9bec994723a657cce8bf708d83879a50c0ce/lib/stores/random.js
    // which in turn based from the demo example at https://www.hsluv.org/syntax/.
    type Colors = HashMap<String, String>;

    #[derive(Serialize, Deserialize, Debug)]
    struct SpecialColors {
      background: String,
      foreground: String,
      cursor: String,
    }

    impl SpecialColors {
      pub fn new() -> Self {
        Self {
          background: String::from("#000000"),
          foreground: String::from("#000000"),
          cursor: String::from("#000000")
        }
      }
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct PywalObject {
      #[serde(default)]
      alpha: u8,

      special: SpecialColors,

      colors: Colors,

      wallpaper: Option<String>,
    }

    impl PywalObject {
      pub fn new() -> Self {
        Self {
          alpha: 255,
          special: SpecialColors::new(),
          colors: Colors::new(),
          wallpaper: None
        }
      }
    }

    /// The command-line interface simply needs a seed to be parsed as an unsigned 64-bit integer (`u64`).
    fn main() -> Result<(), Box<dyn Error>> {
      let args: Vec<String> = env::args().collect();
      let mut result = PywalObject::new();

      let mut rng = match args.get(1) {
        Some(arg) => ChaCha8Rng::seed_from_u64(arg.parse::<u64>()?),
        None => ChaCha8Rng::from_entropy()
      };

      // 6 hues to pick from
      let base_hue: f64 = rng.gen_range(0.0, 360.0);
      let hues: Vec<f64> = ([0.0, 60.0, 120.0, 180.0, 240.0, 300.0])
            .iter()
            .map(|offset| (base_hue + offset) % 360.0)
            .collect();

      //  8 shades of low-saturated color
      let base_saturation: f64 = rng.gen_range(8.0, 40.0);
      let base_lightness: f64 = rng.gen_range(0.0, 10.0);
      let range_lightness: f64 = 90.0 - base_lightness;

      result.special.background = hsluv::hsluv_to_hex(
        (hues[0],
        base_saturation,
        base_lightness / 2.0)
      );

      result.special.foreground = hsluv::hsluv_to_hex(
        (hues[0],
        base_saturation,
        range_lightness)
      );

      result.special.cursor = result.special.foreground.clone();

      // Setting the colors according to the Base16 spec at http://chriskempson.com/projects/base16.
      for i in 0..7 {
        result.colors.insert(format!("color{}", i), hsluv::hsluv_to_hex(
          (hues[0], base_saturation, base_lightness + (range_lightness * ((i as f64)/7.0).powf(1.5)))
        ));
      }

      // 8 random shades
      let min_saturation: f64 = rng.gen_range(30.0, 70.0);
      let max_saturation: f64 = min_saturation + 30.0;
      let min_lightness: f64 = rng.gen_range(50.0, 70.0);
      let max_lightness: f64 = min_lightness + 20.0;

      for j in 8..16 {
        result.colors.insert(format!("color{}", j), hsluv::hsluv_to_hex(
          (hues[rng.gen_range(0, hues.len() - 1)], rng.gen_range(min_saturation, max_saturation), rng.gen_range(min_lightness, max_lightness))
        ));
      }

      println!("{:?}", result);
      Ok(())
    }

- HSLuv

- This introductory article is a great resource on HSLuv

- Color spaces for human beings

- Human-friendly color scheme generation implementation based on terminal.sexy.

## Backlinks

- Generate a color scheme based from a single color