Color
The color/ registry collects small pure WGSL functions for color-space
conversions, channel adjustments, and tone mapping. Each entry below is a
single function you can pull into a composition by slug; the function
itself never owns a vertex or fragment stage, so you decide how the result
gets shown on screen.
A typical use is to chain helpers in your fragment stage — convert into a working space (sRGB or linear), reshape with grading or saturation, then tonemap before you write to the swap chain:
let main = r#"@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { let lin = srgb_to_linear(input.rgb); let graded = saturation(contrast(lin, 1.1), 1.2); return vec4<f32>(linear_to_srgb(tonemap_aces(graded)), 1.0);}"#;let shader = Shader::new(&[ "color/srgb_to_linear", "color/linear_to_srgb", "color/contrast", "color/saturation", "color/tonemap_aces", main,])?;Each preview below renders the function on a 256×256 frame: most show an input gradient on top and the transformed output below, separated by a 2-pixel gutter. Tone-mapping previews use a synthetic 0..6× HDR ramp.
contrast
Section titled “contrast”Linear contrast around mid-gray (0.5). c: color, k: factor (1 = identity).
fn contrast(c: vec3<f32>, k: f32) -> vec3<f32>
hsl_to_rgb
Section titled “hsl_to_rgb”H, S, L in [0, 1] to linear-space RGB in [0, 1].
fn hsl_to_rgb(hsl: vec3<f32>) -> vec3<f32>
hsv_to_rgb
Section titled “hsv_to_rgb”H, S, V in [0, 1] to RGB in [0, 1].
fn hsv_to_rgb(hsv: vec3<f32>) -> vec3<f32>
hue_shift
Section titled “hue_shift”Rotate the hue of a linear-RGB color by radians via a YIQ-style rotation
matrix. Cheap and stays in RGB — handy when you just need a wash without
a round-trip through HSV.
fn hue_shift(c: vec3<f32>, radians: f32) -> vec3<f32>
kelvin_to_rgb
Section titled “kelvin_to_rgb”Approximate blackbody color temperature in Kelvin to linear RGB
(1000..40000 K). Tanner Helland’s piecewise approximation.
fn kelvin_to_rgb(k: f32) -> vec3<f32>
lift_gamma_gain
Section titled “lift_gamma_gain”DaVinci-style color grade. lift and gain scale shadows and highlights;
gamma shapes the midtones. Typical ranges: lift [-0.5, 0.5], gamma
[0.1, 3.0], gain [0.5, 2.0].
fn lift_gamma_gain(c: vec3<f32>, lift: vec3<f32>, gamma: vec3<f32>, gain: vec3<f32>) -> vec3<f32>
linear_srgb_to_oklab
Section titled “linear_srgb_to_oklab”Linear-light sRGB to Björn Ottosson’s OkLab — a perceptually uniform space. Ideal for interpolation, lightness adjustments, and chroma work.
fn linear_srgb_to_oklab(c: vec3<f32>) -> vec3<f32>
linear_to_srgb
Section titled “linear_to_srgb”The inverse sRGB EOTF. Encode a linear-light value before writing it to a non-linear (sRGB) output surface.
fn linear_to_srgb(c: vec3<f32>) -> vec3<f32>
luminance
Section titled “luminance”Rec. 709 relative luminance of a linear-RGB color. The standard scalar weight set for “perceived brightness” in HD video.
fn luminance(c: vec3<f32>) -> f32
oklab_to_linear_srgb
Section titled “oklab_to_linear_srgb”Inverse of linear_srgb_to_oklab. Converts OkLab (L, a, b) back to
linear-light sRGB. Out-of-gamut inputs are returned unclamped.
fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32>
oklch_to_linear_srgb
Section titled “oklch_to_linear_srgb”OkLCh — polar OkLab with (L, C, h) and h in radians — to linear sRGB.
Lets you treat chroma and hue as independent axes when grading.
fn oklch_to_linear_srgb(lch: vec3<f32>) -> vec3<f32>
rgb_to_hsl
Section titled “rgb_to_hsl”RGB in [0, 1] to (H, S, L) in [0, 1].
fn rgb_to_hsl(c: vec3<f32>) -> vec3<f32>
rgb_to_hsv
Section titled “rgb_to_hsv”RGB in [0, 1] to (H, S, V) in [0, 1]. Sam Hocevar’s branchless form.
fn rgb_to_hsv(c: vec3<f32>) -> vec3<f32>
saturation
Section titled “saturation”Adjust saturation by mixing toward Rec.709 luminance. s < 1
desaturates, s > 1 amplifies, s == 0 produces grayscale.
fn saturation(c: vec3<f32>, s: f32) -> vec3<f32>
srgb_to_linear
Section titled “srgb_to_linear”The IEC 61966-2-1 sRGB EOTF, applied component-wise. Decode a non-linear sRGB value back to linear-light before doing math on it.
fn srgb_to_linear(c: vec3<f32>) -> vec3<f32>
tonemap_aces
Section titled “tonemap_aces”Krzysztof Narkowicz’s fitted ACES curve. Expects linear HDR input. Good default for cinematic output.
fn tonemap_aces(c: vec3<f32>) -> vec3<f32>
tonemap_filmic
Section titled “tonemap_filmic”The Jim Hejl / Richard Burgess-Dawson filmic curve. Outputs gamma-encoded values directly — no separate sRGB encode step needed.
fn tonemap_filmic(c: vec3<f32>) -> vec3<f32>
tonemap_reinhard
Section titled “tonemap_reinhard”The classic Reinhard operator: c / (1 + c). Cheap and monotonic, but
washes highlights. Useful baseline.
fn tonemap_reinhard(c: vec3<f32>) -> vec3<f32>
tonemap_uncharted2
Section titled “tonemap_uncharted2”John Hable’s Uncharted 2 operator with an explicit white-point scale. More film-like roll-off than Reinhard at higher cost.
fn tonemap_uncharted2(c: vec3<f32>) -> vec3<f32>