Palettes & ramps
Turn one 0–1 value into a color with multi-stop ramps, cosine palettes or a gradient strip texture.
How it works
Most shaders compute a single number first, a distance, a noise value, a height, a dot productMultiplies two vectors component by component and adds the results. For unit vectors it is the cosine of the angle between them, from 1 (same direction) to -1 (opposite). Dot product Multiplies two vectors component by component and adds the results. For unit vectors it is the cosine of the angle between them, from 1 (same direction) to -1 (opposite).dot(a, b) = |a| |b| cos θt between 0 and 1
to a color. Keeping it separate means one pattern can be recolored a dozen ways, and the color choices stay in one
place.
Two stops and more
The smallest palette is a mixBlends from a to b by a factor t, along a straight line. GLSL calls it mix; HLSL and most node editors call it lerp. Linear interpolation (mix) Blends from a to b by a factor t, along a straight line. GLSL calls it mix; HLSL and most node editors call it lerp.mix(a, b, t) = a (1 − t) + b t
vec3 ramp3(vec3 a, vec3 b, vec3 c, float t) {
return t < 0.5 ? mix(a, b, t * 2.0) : mix(b, c, t * 2.0 - 1.0);
}
Any number of evenly spaced stops fit in an array. Scale t to the number of segments, take the whole part as the
segment and the fractional part as the position inside it:
const vec3 STOPS[4] = vec3[4](
vec3(0.02, 0.01, 0.05), // night
vec3(0.45, 0.05, 0.20), // plum
vec3(0.95, 0.40, 0.10), // ember
vec3(1.00, 0.90, 0.60)); // flame
vec3 ramp(float t) {
float x = clamp(t, 0.0, 1.0) * 3.0; // 3 segments between 4 stops
int i = int(min(floor(x), 2.0)); // last segment includes t = 1
return mix(STOPS[i], STOPS[i + 1], x - float(i));
}
Swap the mix for a smoothstep of the position to soften the corners where segments meet, or for step to get flat
bands, a cheap posterized look.
Cosine palettes
A cosine palette, a form popularized by Inigo Quilez, describes a whole color scheme with four vec3 parameters and no
stops at all. Each channel follows its own cosine wave over t:
vec3 cosinePalette(float t, vec3 offset, vec3 amp, vec3 freq, vec3 phase) {
return offset + amp * cos(6.2831853 * (freq * t + phase));
}
Read the parameters per channel, as a wave from the periodic-functions page: offset is the center the channel swings
around, amp how far it swings, freq how many cycles it makes as t goes from 0 to 1, and phase where in its cycle
it starts. Giving the three channels different values is what produces hues; with the same four values on every
channel they move together and the result stays gray.
// a sunset ramp: deep red through orange to pale yellow
vec3 sunset = cosinePalette(t, vec3(0.70, 0.485, 0.325), vec3(0.30, 0.465, 0.275),
vec3(0.5), vec3(0.50, 0.45, 0.40));
Keep offset ± amp inside 0–1 on every channel and the palette never clips. Because the waves repeat, t can run past
1: feed in a distance or a scrolling t + time * 0.1 and the colors cycle smoothly forever. A palette with freq of
1 on every channel loops seamlessly over each unit of t.
Gradient strips
When an artist has already painted the gradient, store it as a texture one texel tall and read it with t as the
horizontal coordinate:
// at file scope:
uniform sampler2D uRamp; // N×1 texels, clamp to edge
// inside main():
vec3 color = texture(uRamp, vec2(t, 0.5)).rgb;
Linear filtering blends between texels, and nearest filtering gives hard bands, which suits palettes for pixel art.
Clamp to edge keeps the two ends from blending into each other. With linear filtering the first and last half texels
are flat; to spread the stops evenly from end to end, read at (t * (N - 1.0) + 0.5) / N instead. A strip costs a
texture read but can hold any curve, and a taller texture can store several palettes, one per row, picked by a second
coordinate.
Which values to pick
Palette colors are usually chosen by eye, as you would in a paint program, so they are encoded
sRGBThe standard encoding of screen colors. Stored values follow a curve close to a 1/2.2 power of the light, so they must be decoded before lighting or blending. sRGB The standard encoding of screen colors. Stored values follow a curve close to a 1/2.2 power of the light, so they must be decoded before lighting or blending.linear = ((c + 0.055) / 1.055)^2.4 for c > 0.04045