Skip to main content
colorscope

Harmony

Generate harmonious color schemes by rotating hue in OKLCH space. All rotations preserve the source color's lightness and chroma.

Harmony Type

15°base
#c54920
HSL(15, 72%, 45%)

Generated Colors

#c54920Base
#008bb7218°
generateHarmony(
  { hue: 15, saturation: 72, lightness: 45 },
  "complementary"
);
// => [
  "#008bb7"
]

Tonal Palette

8 role-based UI colors generated from the base hue. Hover to expand.

primary
onPrimary
primaryContainer
secondary
surface
onSurface
accent
muted
generateTonalPalette(15);
// => [
  primary: "#db6371",
  onPrimary: "#fdf6f7",
  primaryContainer: "#271c1c",
  secondary: "#dd999d",
  surface: "#191515",
  onSurface: "#e7e3e3",
  accent: "#d3983c",
  muted: "#7c6e6e"
]
import { generateHarmony, complementary, analogous } from "colorscope/harmony";

Functions

generateHarmony

Generate harmony colors by type. Dispatches to the specific generator.

function generateHarmony(input: HarmonyInput, type: HarmonyType): HarmonyColor[]
ParamTypeDescription
inputHarmonyInputSource color
typeHarmonyTypeHarmony scheme to generate

Returns: Array of HarmonyColor objects (does not include the input color).

const input = { hue: 0, saturation: 100, lightness: 50 }; // red
const harmony = generateHarmony(input, "complementary");
// [{ oklab: {...}, oklch: {...}, hex: "#00b3b3" }]

complementary

Generate the complementary color (180° rotation). Returns 1 color.

function complementary(input: HarmonyInput): [HarmonyColor]

analogous

Generate analogous colors (±angle rotation). Returns 2 colors adjacent on the wheel.

function analogous(input: HarmonyInput, angle?: number): [HarmonyColor, HarmonyColor]
ParamTypeDefaultDescription
inputHarmonyInputSource color
anglenumber30Rotation angle in degrees

triadic

Generate triadic colors (±120° rotation). Returns 2 colors forming an equilateral triangle.

function triadic(input: HarmonyInput): [HarmonyColor, HarmonyColor]

splitComplementary

Generate split-complementary colors (180° ± angle). Returns 2 colors flanking the complement.

function splitComplementary(
  input: HarmonyInput,
  angle?: number
): [HarmonyColor, HarmonyColor]
ParamTypeDefaultDescription
inputHarmonyInputSource color
anglenumber30Split angle from complement

tetradic

Generate tetradic colors (90° apart). Returns 3 colors forming a rectangle with the source.

function tetradic(input: HarmonyInput): [HarmonyColor, HarmonyColor, HarmonyColor]

generateTonalPalette

Generate a tonal palette from a single base hue. Unlike classical harmony (which rotates hue at constant lightness/chroma), a tonal palette assigns each color a UI role with appropriate lightness and chroma — producing palettes that work in real interfaces.

function generateTonalPalette(baseHue: number, baseChroma?: number): TonalColor[]
ParamTypeDefaultDescription
baseHuenumberHue angle in degrees (0-360)
baseChromanumber0.15Maximum chroma for the primary role

Returns: 8 role-based TonalColor objects.

import { generateTonalPalette } from "colorscope/harmony";

const palette = generateTonalPalette(220); // blue-based palette
// [
//   { role: "primary",          hex: "#5b8fd4", ... },
//   { role: "onPrimary",        hex: "#f5f7fa", ... },
//   { role: "primaryContainer", hex: "#1a2a3d", ... },
//   { role: "secondary",        hex: "#8ba8c9", ... },
//   { role: "surface",          hex: "#1c2028", ... },
//   { role: "onSurface",        hex: "#e5e8ec", ... },
//   { role: "accent",           hex: "#c9a55b", ... },
//   { role: "muted",            hex: "#6b7078", ... },
// ]

The 8 roles:

  • primary — Main brand/accent color
  • onPrimary — Text/icons on primary surfaces
  • primaryContainer — Darker container for primary content
  • secondary — Supporting color at lower chroma
  • surface — Dark background surface
  • onSurface — Readable text color for surfaces
  • accent — Complementary hue (60° offset) for visual interest
  • muted — Low-chroma supporting tone for borders/dividers

Types

HarmonyType

type HarmonyType =
  | "complementary"
  | "analogous"
  | "triadic"
  | "split-complementary"
  | "tetradic"

HarmonyInput

interface HarmonyInput {
  hue: number;        // HSL hue (0-360)
  saturation: number;  // HSL saturation (0-100)
  lightness: number;   // HSL lightness (0-100)
  oklab?: OKLab;       // Optional pre-computed OKLab (used if provided)
}

QuantizedColor satisfies this interface, so you can pass extracted colors directly.

HarmonyColor

interface HarmonyColor {
  oklab: OKLab;   // OKLab values
  oklch: OKLCH;   // OKLCH values (cylindrical)
  hex: string;    // Hex color string
}

TonalRole

type TonalRole =
  | "primary"
  | "onPrimary"
  | "primaryContainer"
  | "secondary"
  | "surface"
  | "onSurface"
  | "accent"
  | "muted"

TonalColor

interface TonalColor {
  role: TonalRole;   // The UI role this color fulfills
  oklch: OKLCH;      // OKLCH representation (L/C tuned per role)
  oklab: OKLab;      // OKLab representation
  hex: string;       // sRGB hex string, gamut-clamped
}