Palette
Palette is a list of colors with helpers for pulling entries out of it in a controlled way. It generates colors in OKLCH, so a fixed lightness reads as the same lightness across every hue.
public/lib/Palette.js, public/lib/color.js — copied from the stroke_designer project. public/lib/SchemePaletteMaker.js builds on them.
Gamut
OKLCH describes colors sRGB cannot display. maxChromaAt(L, H) binary-searches the largest chroma that stays inside the sRGB gamut at a given lightness and hue, and generation never asks for more than that. mostVibrantL(H) scans for the lightness at which a hue reaches its highest chroma. Both live in color.js.
Generating
Palette.fromHues(hues, options) returns one entry per hue × luminosity step.
nLum— luminosity steps per hue. Default4.
色相あたりの明度ステップ数。既定値は4。lumHigh/lumLow— the lightness range the steps span. Defaults0.85/0.30.
ステップが分布する明度の範囲。既定値は0.85/0.30。vibHigh/vibLow— the ends of the chroma multiplier range. Defaults0.95/0.30. See Chroma below, since neither is applied directly.
彩度の倍率の範囲の両端。既定値は0.95/0.30。どちらもそのまま適用されるわけではないため、下記のChromaを参照してください。
Steps are not spaced evenly between lumLow and lumHigh. The generator finds the lightness at which that hue reaches its maximum chroma (mostVibrantL(H)), snaps the nearest step to it, then redistributes the remaining steps on either side. Every hue therefore contributes one entry at the lightness where it can be most chromatic. That entry is not at the hue’s maximum chroma, because the multiplier below applies to it like any other.
The first and last entries normally land exactly on lumHigh and lumLow. They do not when the snapped step is itself the first or last one, in which case that end takes the vibrant lightness and the requested bound is unused. At nLum: 5, lumHigh: 0.88, lumLow: 0.28 this happens at hues 80, 90, 140, 150, 160, and 200.
Chroma
Each entry starts from maxChromaAt(L, H) and takes a fraction of it. The fraction varies per entry, rising with how chromatic that lightness and hue could be.
t = min(1, maxC / 0.4)
C = maxC × (vibLow + (vibHigh − vibLow) × t)
0.4 is the reference for the most chromatic color the model expects to meet. vibHigh is therefore a ceiling rather than a setting: it applies only where maxC reaches 0.4, which sRGB does not do. At the defaults the largest fraction any entry takes is about 0.78, and no entry anywhere in the palette sits at its own maxC.
The multiplier rises with maxC, so the entry with the most headroom stays the most chromatic of its row. Setting vibLow above vibHigh inverts that relationship and the ordering no longer holds.
Two other constructors take colors that already exist: fromHexArray(hexes) for plain strings, and fromEntries(entries) for objects that already carry L, C, and H.
SchemePaletteMaker
SchemePaletteMaker generates a Palette from three inputs: a key hue, a color count, and a scheme. The scheme decides everything else: which hues are used, and how light and how saturated each color is. new SchemePaletteMaker({ hue, count, scheme, seed }).generate() returns a regular Palette, with the key color first.
mono— every color at the key hue. The first at the hue's most vibrant point, the rest dividing L as evenly as that fixed first color allows, each at the largest chroma available.
すべての色は基準の色相に揃えられます。最初の色は、その色相が最も鮮やかになる点に配置され、残りの色は、その最初の色が許す範囲で明度の幅を均等に分割して配置します。彩度は、各明度で取りうる最大値です。vivid-dark— the whole wheel divided evenly from the key hue; the half nearest the key hue vivid at each hue's representative lightness, the far half dark.
色相環全体を基準の色相から均等に分割します。基準に近い半分は各色相の代表的な明度で鮮やかな色になり、遠い半分は暗い色になります。pastel-cluster— hues clustered around the key hue, all light and low in chroma.
色相を基準の色相のまわりに集め、すべて明るく、彩度の低い色にします。dark-cluster— the same clustering, tighter, every color dark.
同じクラスタをより狭くし、すべての色を暗くしたものです。vivid-wheel— the whole wheel divided evenly, every color at its own hue's most vibrant point.
色相環全体を均等に分割し、各色をその色相が最も鮮やかになる点に配置します。black— every color black.
すべての色が黒です。
Every scheme except black jitters the hue and lightness of the colors other than the key color. The jitter derives from seed, so the same settings reproduce the same palette. PALETTE_SCHEMES lists the schemes with display labels.
representativeL(H) is the lightness of a hue’s most prototypical color, the one people recognize by the simplest color term (red, green, blue, pink). That color sits at a particular lightness, not at the hue’s max-chroma point: yellow is only yellow when bright, while green and blue are their names well below their chroma peaks. Anchored per color term (red 0.58, orange 0.72, yellow 0.90, yellow-green 0.82, green 0.50, cyan 0.70, blue 0.45, purple 0.45, magenta 0.58, pink 0.78) and interpolated around the wheel. The prototypes are cultural: which terms are basic, and where their colors sit, varies between cultures, and the anchors are one such choice.
Three helpers support the demos: paperColor(hue) returns a near-white paper tint of a hue for a background, paperGradient(palette) returns a two-color paper-tint gradient spec for a board clear, and randomSchemePalette(scheme, count) returns a schemed palette at a random key hue and seed.
Selecting
spread(n)—nentries evenly distributed across the palette. Every hue and step stays represented no matter how many entries the settings produced.
パレット全体から等間隔にn個のエントリを取ります。設定によってエントリ数がどう変わっても、すべての色相とステップが漏れなく含まれます。sample(t)— the entry at fractional positiont.
位置t(0〜1)にあるエントリ。pickPair(i)— an entry and its counterpart roughly half-way round the palette, for a two-color gradient. Deterministic wheniis given.
あるエントリと、パレット上でほぼ半周先にあるエントリの組。2色のグラデーション用です。iを指定すれば決定論的になります。pick()— a random entry.
ランダムなエントリ。toHexArray(),entries,length— the raw list.
生のリストへのアクセス。
pick() and a bare pickPair() call Math.random(). Pass an index to pickPair(i) for a deterministic result.
pick() と引数なしの pickPair() は Math.random() を呼びます。決定論的な結果が必要な場合は pickPair(i) にインデックスを渡してください。