core

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package core is the heart of wondertone.

Import it as "tone" for the most natural developer experience:

import tone "github.com/leraniode/wondertone/core"

spark := tone.New(
    tone.Light(75),
    tone.Vibrancy(80),
    tone.Hue(30),
    tone.Energy(0.9),
    tone.Named("Primary Spark"),
    tone.Moody("vibrant"),
)

Wondertone speaks in Light, Vibrancy, Hue, and Energy. OKLCH is the engine. You never have to know it exists.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Arousal added in v0.2.0

func Arousal(saturation, energy, tv float64) float64

Arousal returns the emotional arousal/energy level of a tone ∈ [-1, +1].

+1.0 = highly activated (vivid, high energy)
-1.0 = calm/sleepy (muted, low energy)

Formula:

arousal = clamp(b1*S + b2*E + b3*T, -1, 1)

func CorrectedHue added in v0.2.0

func CorrectedHue(h, c, l float64) float64

CorrectedHue applies the WonderMath blue-drift correction. Returns H' — the perceptually corrected hue.

Formula:

H' = wrap360(H + A * exp(-(H-H0)² / (2w²)) * (C / C_max))

The Gaussian is chroma-weighted: grey tones get zero correction. Only vivid blues near H≈250° are nudged back from purple.

func DerivedMood added in v0.2.0

func DerivedMood(valence, arousal, temperatureValue float64) string

DerivedMood maps a valence/arousal vector to a named mood. This replaces the manual Moody() tag with a mathematically derived value. The manually set mood still takes precedence as a display override.

Mood regions (valence, arousal):

vivid    — high arousal, mid-high valence
playful  — high arousal, high valence, warm
urgent   — high arousal, low-mid valence
focused  — mid arousal, mid valence
warm     — mid arousal, high valence, warm temperature
mystical — mid arousal, low valence, cool
calm     — low arousal, mid valence
deep     — low arousal, low valence
airy     — low arousal, high valence

func EffectiveChroma added in v0.2.0

func EffectiveChroma(c, energy float64) float64

EffectiveChroma applies the perceptually linear Energy scaling to chroma.

Formula:

C_effective = C_base * E^γ

γ ≈ 0.7 (Stevens' power law exponent for colour saturation). This makes Energy=0.5 feel like "half as alive", not "60% as alive".

func EffectiveLightness added in v0.2.0

func EffectiveLightness(l, energy float64) float64

EffectiveLightness applies the subtle lightness glow at high energy. High-energy tones get a small brightness boost — they glow slightly.

Formula:

L_effective = L + λ * (E^γ - 1)

λ = 0.04 means full-energy tone gets +4% lightness max. At Energy=0.5: L_effective = L + 0.04 * (0.5^0.7 - 1) ≈ L - 0.015

func PerceivedChroma added in v0.2.0

func PerceivedChroma(vibrancy, l, h float64) float64

PerceivedChroma converts Vibrancy [0–100] to raw OKLCH chroma C using the full WonderMath pipeline:

  1. Gamut-relative base: rawC = C_max * (V/100)^α
  2. Hue weight correction: C = rawC * k(H)

Energy is NOT applied here — it is applied separately in EffectiveC. This is the stored chroma value: what the tone IS, not how it renders.

func TemperatureLabel added in v0.2.0

func TemperatureLabel(tv float64) string

TemperatureLabel maps a continuous temperature value to "warm", "cool", or "neutral". Replaces the old hue-range lookup.

func TemperatureValue added in v0.2.0

func TemperatureValue(h, c, l float64) float64

TemperatureValue returns a continuous warm↔cool scalar T ∈ [-1, +1].

+1.0 = maximally warm (vivid red-orange)
 0.0 = neutral
-1.0 = maximally cool (vivid blue-cyan)

Formula:

raw = cos((H - H_warm) * π/180)
T = clamp(w_h*raw + w_c*(C/C_max) + w_l*(L-0.5), -1, 1)

Hue dominates (w_h=0.7). Chroma boosts the reading (more vivid = more definitely warm or cool). Lightness nudges slightly (dark oranges read slightly cooler than bright ones).

func Valence added in v0.2.0

func Valence(tv, l, saturation float64) float64

Valence returns the emotional valence of a tone ∈ [-1, +1].

+1.0 = most positive (bright, light, warm)
-1.0 = most negative (dark, cool, desaturated)

Formula (Valdez & Mehrabian style):

valence = clamp(a1*T + a2*L + a3*S, -1, 1)

Types

type Option

type Option func(*Tone)

Option configures a Tone during construction.

func Alpha

func Alpha(a float64) Option

Alpha sets the opacity [0–1]. Default is 1.0 (fully opaque).

func Energy

func Energy(v float64) Option

Energy sets the aliveness multiplier [0–1]. 1.0 = full vivid (default). 0.5 = half as colorful, same hue. 0.0 = grey. Energy scales effective chroma at render time without changing the stored Tone. A palette can whisper or shout — same colors, different energy.

func Hue

func Hue(v float64) Option

Hue sets the color angle [0–360). 0/360=red, 30=orange, 60=yellow, 120=green, 180=cyan, 240=blue, 300=magenta.

func Light

func Light(v float64) Option

Light sets the perceptual lightness [0–100]. 0 is black. 100 is white. 50 is a true mid-tone.

func Moody

func Moody(mood string) Option

Moody gives the Tone a mood tag. Examples: "vibrant", "serene", "mystical", "focused", "playful", "warm".

func Named

func Named(name string) Option

Named gives the Tone a human name.

func Vibrancy

func Vibrancy(v float64) Option

Vibrancy sets how colorful the tone is [0–100], relative to the maximum possible saturation at this lightness and hue. 0 is pure grey. 100 is the most vivid color the sRGB gamut allows at this exact Light+Hue combination.

This is not raw OKLCH chroma — it is a percentage of the gamut ceiling. This means Vibrancy(80) always looks 80% vivid regardless of hue.

type Tone

type Tone struct {
	// contains filtered or unexported fields
}

Tone is the atomic unit of wondertone — a living, named color.

Developers work with the Wondertone vocabulary:

  • Light [0–100] perceptual lightness. 0=black, 100=white.
  • Vibrancy [0–100] colorfulness relative to gamut max. 0=grey, 100=most vivid possible.
  • Hue [0–360) color angle. 0=red, 120=green, 240=blue.
  • Energy [0–1] aliveness multiplier. 0=muted/sleeping, 1=full/vivid.

Under the hood, everything is OKLCH — perceptually uniform, gamut-safe, hue-preserving. The developer never touches L, C, H directly unless they want to via FromOKLCH.

A Tone is immutable. Every method returns a new Tone.

func Blend

func Blend(tones []Tone, weights []float64) (Tone, error)

Blend mixes multiple Tones with given weights in OKLab space. weights must be the same length as tones and sum > 0. Weights are normalized automatically — you don't need to sum to 1.

func FromHex

func FromHex(s string) (Tone, error)

FromHex parses a CSS hex string (#rgb, #rrggbb, #rrggbbaa).

func FromOKLCH

func FromOKLCH(l, c, h float64) Tone

FromOKLCH creates a Tone directly from raw OKLCH values. This is the power-user escape hatch — use New() for the normal path. L [0–1], C [0–~0.37], H [0–360).

func FromOKLCHString

func FromOKLCHString(s string) (Tone, error)

FromOKLCHString parses an oklch string: "0.75 0.15 30" or "0.75 0.15 30 / 0.9".

func Gradient

func Gradient(start, end Tone, n int) ([]Tone, error)

Gradient produces n evenly spaced Tones between start and end in OKLab space. n must be >= 2. The first element is start, the last is end. No grey midpoint artifacts — OKLab interpolation guarantees perceptual smoothness.

func Harmonize

func Harmonize(base Tone, scheme string) ([]Tone, error)

Harmonize returns a set of Tones related by the given harmonic scheme. All harmony is computed by hue rotation in OKLCH — lightness and vibrancy are preserved from the base Tone.

scheme: "complement", "triadic", "analogous", "split", "tetradic"

func Mix

func Mix(a, b Tone, t float64) Tone

Mix blends two Tones in OKLab space at ratio t [0–1]. t=0 returns a, t=1 returns b, t=0.5 is the perceptual midpoint.

OKLab gives straight-line interpolation — no unexpected hue swings through grey that you get with HSL or even OKLCH mixing. The result name and mood are cleared (the blend is a new thing).

func MustFromHex

func MustFromHex(s string) Tone

MustFromHex parses a hex string and panics on error. Use only for known-good compile-time constants (e.g. in colour/ package).

func New

func New(opts ...Option) Tone

New creates a Tone from options using the Wondertone vocabulary.

spark := tone.New(
    tone.Light(75),
    tone.Vibrancy(80),
    tone.Hue(30),
    tone.Energy(0.9),
    tone.Named("Primary Spark"),
)

func Shift

func Shift(t Tone, direction string, amount float64) (Tone, error)

Shift nudges a Tone toward warmer or cooler territory. direction: "warmer" or "cooler". amount [0–1].

func (Tone) AlphaValue

func (t Tone) AlphaValue() float64

AlphaValue returns the opacity [0–1].

func (Tone) ArousalValue added in v0.2.0

func (t Tone) ArousalValue() float64

ArousalValue returns the emotional arousal of this tone ∈ [-1, +1]. +1 = activated (vivid, energetic). -1 = calm (muted, quiet).

func (Tone) Complement

func (t Tone) Complement() Tone

Complement returns the Tone directly opposite on the hue wheel (+180°).

func (Tone) ContrastWith

func (t Tone) ContrastWith(other Tone) float64

ContrastWith returns the WCAG 2.1 contrast ratio against another Tone [1–21].

func (Tone) Darken

func (t Tone) Darken(amount float64) Tone

Darken returns a new Tone with lightness decreased by amount [0–100].

func (Tone) DerivedMoodValue added in v0.2.0

func (t Tone) DerivedMoodValue() string

DerivedMoodValue returns the mathematically derived mood string. Computed from valence and arousal — independent of the stored Mood() tag. Use Mood() for the display label (manual override takes precedence).

func (Tone) Desaturate

func (t Tone) Desaturate(amount float64) Tone

Desaturate returns a new Tone with vibrancy decreased by amount [0–100].

func (Tone) EffectiveC

func (t Tone) EffectiveC() float64

EffectiveC returns the chroma that will actually be used for rendering. Uses the WonderMath Stevens' power law: C * E^γ (γ≈0.7). Energy=0.5 now feels half as alive, not 60% as alive. The stored C and Energy are never modified.

func (Tone) Energy

func (t Tone) Energy() float64

Energy returns the aliveness multiplier [0–1].

func (Tone) EnsureContrast

func (t Tone) EnsureContrast(bg Tone, level string) Tone

EnsureContrast returns a Tone adjusted to meet the target contrast ratio against bg. Adjusts lightness only — hue, vibrancy and energy are preserved. level: "AA" (4.5:1) or "AAA" (7.0:1).

func (Tone) Equal

func (t Tone) Equal(other Tone) bool

Equal reports whether two Tones are perceptually identical within floating-point tolerance.

func (Tone) Hex

func (t Tone) Hex() string

Hex returns the color as a lowercase CSS hex string. Gamut-safe: out-of-range OKLCH values are mapped into sRGB before encoding.

func (Tone) Hue

func (t Tone) Hue() float64

Hue returns the color angle [0–360).

func (Tone) IsDark

func (t Tone) IsDark() bool

IsDark reports whether the Tone is perceptually dark (Light <= 50).

func (Tone) IsLight

func (t Tone) IsLight() bool

IsLight reports whether the Tone is perceptually light (Light > 50).

func (Tone) Light

func (t Tone) Light() float64

Light returns the perceptual lightness [0–100].

func (Tone) Lighten

func (t Tone) Lighten(amount float64) Tone

Lighten returns a new Tone with lightness increased by amount [0–100].

func (Tone) Luminance

func (t Tone) Luminance() float64

Luminance returns the WCAG 2.1 relative luminance [0–1].

func (Tone) Mood

func (t Tone) Mood() string

Mood returns the tone's mood tag.

func (Tone) Name

func (t Tone) Name() string

Name returns the tone's human name.

func (Tone) OKLCH

func (t Tone) OKLCH() (l, c, h float64)

OKLCH returns the raw internal OKLCH values: L [0–1], C [0–~0.37], H [0–360).

func (Tone) OKLCHString

func (t Tone) OKLCHString() string

OKLCHString returns the canonical OKLCH string: "0.750000 0.150000 30.000000". This is what .wtone files store — full precision, no loss.

func (Tone) PassesAA

func (t Tone) PassesAA(bg Tone) bool

PassesAA reports whether this Tone meets WCAG AA (4.5:1) against bg.

func (Tone) PassesAAA

func (t Tone) PassesAAA(bg Tone) bool

PassesAAA reports whether this Tone meets WCAG AAA (7.0:1) against bg.

func (Tone) RGB

func (t Tone) RGB() (r, g, b uint8)

RGB returns gamma-encoded sRGB values [0–255].

func (Tone) RGBFloat

func (t Tone) RGBFloat() (r, g, b float64)

RGBFloat returns gamma-encoded sRGB values [0.0–1.0].

func (Tone) Rotate

func (t Tone) Rotate(degrees float64) Tone

Rotate returns a new Tone with hue rotated by degrees. Accepts negative values (counter-clockwise).

func (Tone) Saturate

func (t Tone) Saturate(amount float64) Tone

Saturate returns a new Tone with vibrancy increased by amount [0–100].

func (Tone) Scale

func (t Tone) Scale() ToneScale

Scale returns the 12-step perceptual tone scale for this Tone. Step 9 is the "pure" tone — closest to the original.

func (Tone) Step

func (t Tone) Step(n int) Tone

Step returns a single step from the 12-step scale [1–12]. 1=lightest, 12=darkest.

func (Tone) String

func (t Tone) String() string

String implements fmt.Stringer — returns hex representation.

func (Tone) Temperature

func (t Tone) Temperature() string

Temperature returns "warm", "cool", or "neutral". Upgraded in v0.2: uses WonderMath continuous formula instead of hue-range lookup — chroma and lightness now modulate the reading.

func (Tone) TemperatureScalar added in v0.2.0

func (t Tone) TemperatureScalar() float64

TemperatureScalar returns the continuous warm↔cool value T ∈ [-1, +1]. +1 = maximally warm, -1 = maximally cool, 0 = neutral. More precise than Temperature() which returns a label.

func (Tone) ValenceValue added in v0.2.0

func (t Tone) ValenceValue() float64

Valence returns the emotional valence of this tone ∈ [-1, +1]. +1 = positive (bright, warm, vivid). -1 = negative (dark, cool, muted).

func (Tone) Vibrancy

func (t Tone) Vibrancy() float64

Vibrancy returns the colorfulness percentage [0–100].

func (Tone) WithAlpha

func (t Tone) WithAlpha(a float64) Tone

WithAlpha returns a new Tone with the given alpha [0–1].

func (Tone) WithEnergy

func (t Tone) WithEnergy(e float64) Tone

WithEnergy returns a new Tone with the given energy [0–1].

func (Tone) WithHue

func (t Tone) WithHue(h float64) Tone

WithHue returns a new Tone with the given hue [0–360).

func (Tone) WithLight

func (t Tone) WithLight(v float64) Tone

WithLight returns a new Tone with the given lightness [0–100].

func (Tone) WithMood

func (t Tone) WithMood(mood string) Tone

WithMood returns a new Tone with the given mood.

func (Tone) WithName

func (t Tone) WithName(name string) Tone

WithName returns a new Tone with the given name.

func (Tone) WithVibrancy

func (t Tone) WithVibrancy(v float64) Tone

WithVibrancy returns a new Tone with the given vibrancy [0–100].

type ToneScale

type ToneScale [12]Tone

ToneScale is a 12-step perceptual ladder from a single base Tone. Hue never drifts. Every step is in sRGB gamut.

Step roles (1=lightest, 12=darkest):

 1  Page background         — barely tinted white
 2  Subtle background       — stripes, alternating rows
 3  UI element background   — cards, inputs
 4  Hovered element bg      — hover state
 5  Active/selected bg      — selected state
 6  Subtle border           — separators
 7  UI border               — input borders
 8  Strong border           — focus rings
 9  Solid bg                — buttons, badges — base tone lives here
10  Hovered solid           — button hover
11  Text                    — readable on light backgrounds
12  High-contrast text      — headings, emphasis

func (ToneScale) ActiveBackground

func (s ToneScale) ActiveBackground() Tone

func (ToneScale) All

func (s ToneScale) All() []Tone

All returns all 12 tones as a slice (lightest to darkest).

func (ToneScale) Background

func (s ToneScale) Background() Tone

Semantic accessors — named by their UI role.

func (ToneScale) Border

func (s ToneScale) Border() Tone

func (ToneScale) ElementBackground

func (s ToneScale) ElementBackground() Tone

func (ToneScale) HighContrastText

func (s ToneScale) HighContrastText() Tone

func (ToneScale) HoveredBackground

func (s ToneScale) HoveredBackground() Tone

func (ToneScale) HoveredSolid

func (s ToneScale) HoveredSolid() Tone

func (ToneScale) Solid

func (s ToneScale) Solid() Tone

func (ToneScale) Step

func (s ToneScale) Step(n int) Tone

Step returns a tone by 1-based step number [1–12]. Clamped at edges.

func (ToneScale) StrongBorder

func (s ToneScale) StrongBorder() Tone

func (ToneScale) SubtleBackground

func (s ToneScale) SubtleBackground() Tone

func (ToneScale) SubtleBorder

func (s ToneScale) SubtleBorder() Tone

func (ToneScale) Text

func (s ToneScale) Text() Tone

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL