Gamma, Linear Light, and Transfer Functions
The values stored in a pixel are not proportional to the light a display emits. A curve called the transfer function sits between file and photon, and getting it wrong - or skipping it - produces every compositing halo, muddy midtone, and "math doesn't work" bug in graphics. This article explains why the curve exists, what shape it takes, and how to use it correctly.
Advanced gamma workbench
Transfer functions are not just curves on a chart. They decide how many code values shadows receive, how a blend behaves, how alpha edges composite, how much HDR luminance survives tone mapping, and where a renderer accidentally double-decodes or double-encodes a signal. This workbench lets you poke the whole pipeline at once.
Decode, operate, quantize, tone-map, and re-encode a signal
Choose a source transfer function, choose the operation you are testing, then move the sliders. The canvas compares encoded-space math with linear-light math and flags the most likely production bug.
Decode before math, do the operation in linear light, then encode for delivery. This panel shows what breaks when that order changes.
Why gamma exists
Two unrelated facts collided in the 20th century to produce gamma encoding. First, the cathode-ray tubes that dominated displays for fifty years had a non-linear response: an input voltage V produced a screen luminance roughly proportional to V2.2. A linear-in-voltage signal looked nothing like a linear-in-light scene. Second, human vision is also non-linear: we are far more sensitive to small differences in dark tones than to small differences in bright ones. A linear-light ramp from black to white spends most of its visual extent looking nearly white.
These two facts almost cancelled. Encoding light through a ≈1/2.2 power curve before sending it to a CRT produced the visually-uniform tones we wanted, and the CRT's own 2.2 response decoded it back to linear light. The "happy accident" became infrastructure. Even though CRTs are gone, every modern display still emulates that response, and every file format still stores values that have been raised to roughly the inverse power.
Display response and perception
The eye's lightness response is approximately a cube root of luminance over a wide range - that's exactly the formula CIE L* uses to perceptually scale Y. A power function with exponent 1/2.2 approximates the cube root closely enough that a roughly perceptual encoding falls out automatically. Modern displays no longer have intrinsic gamma - LCDs are nearly linear; OLEDs are approximately so - but they still simulate the historic CRT response so files stay compatible.
Watch a linear-light ramp look biased toward white
Two horizontal ramps: the top is linear in luminance, the bottom is linear in L*. Equal numerical steps in the linear bar visually compress the dark side into a thin band; equal L* steps look evenly spaced. The eye is the source of the asymmetry - not the display.
The sRGB transfer function
The sRGB transfer function is the one almost every file you touch was encoded with. It approximates a γ ≈ 2.2 curve but with one important refinement: a small linear toe at the very dark end. Pure power curves have infinite slope at zero, which causes numerical instability in quantized arithmetic. The linear toe keeps the curve well-behaved near black.
vencoded = 1.055 · vlinear1/2.4 − 0.055 otherwise sRGB encoding (linear-light → file value). Inverse for decoding.
Notice that the curve uses 1/2.4, not 1/2.2. The piecewise structure - linear toe + power segment - gives an effective exponent close to 2.2 over most of the range. Calling sRGB "gamma 2.2" is shorthand. The piecewise function is the specification; the 2.2 number is an approximation that fails in shadows.
Transfer function catalogue
Several transfer functions show up across modern imaging. Knowing which one your file uses is half the battle.
| Function | Form | Effective γ | Used in |
|---|---|---|---|
| Linear | venc = vlin | 1.0 | Scene-referred rendering, compositing, lighting math |
| sRGB | Piecewise: linear toe + γ 1/2.4 | ~2.2 | Web, JPEG, PNG (default), most consumer photos |
| BT.1886 | Pure γ 2.4 | 2.4 | Rec.709 and SDR Rec.2020 video, broadcast TV, Blu-ray |
| ProPhoto γ 1.8 | Piecewise: linear toe + γ 1/1.8 | 1.8 | ProPhoto RGB archival photo workflow |
| DCI-P3 γ 2.6 | Pure γ 2.6 | 2.6 | Digital cinema theatrical projection |
| Adobe RGB γ 2.2 | Pure γ 2.2 (no toe) | 2.2 | Adobe RGB photo editing |
| PQ (BT.2100) | SMPTE ST 2084 absolute | — | HDR10, Dolby Vision, streaming HDR |
| HLG (BT.2100) | Hybrid log-gamma | — | HDR broadcast (BBC/NHK), backward-compatible |
| Log-C / S-Log3 / V-Log | Logarithmic | — | Cinema cameras, intermediate "scene-log" workspace |
Compare the shapes side by side
Toggle which curves to draw. The plot maps encoded values 0-1 on the x-axis to linear-light values 0-1 on the y-axis (decoding direction). Notice how different the dark end is - the slope at zero determines how many bits each curve "spends" on shadows.
Linear vs encoded RGB
Two completely different things wear the same "RGB" badge. Encoded RGB is what files store - non-linear values shaped by a transfer function. Linear RGB is what physics deals in - values proportional to actual photon counts. Every reasonable color operation lives in one of these two spaces, and the conversion between them is the transfer function.
Why blending must happen in linear
Take 50% red and 50% green at full encoded value: #ff0000 and
#00ff00. Average them naively in their encoded form: you get
#7f7f00, a dark mustard yellow. Average them in linear light, then
re-encode: you get something close to #bcbc00, a bright olive yellow.
The dark answer is physically wrong - it represents less light than either
contributor.
This shows up everywhere. Gaussian blur, image resize, alpha blending, gradient interpolation, anti-aliasing, lighting math, color picker midpoints. Every operation that produces a value between two pixels must compute in linear space and re-encode. Almost every classical 2D graphics library got this wrong for decades; modern libraries are still partially broken.
Mix two colors two different ways
Choose any two colors and the blend amount. The system shows both midpoints: encoded-space (the wrong way) and linear-space (the right way). The difference is most visible with high-saturation complementary pairs.
Same endpoints, two interpolations
A horizontal gradient from one color to another. Top bar uses encoded-space interpolation - what most CSS engines and pre-2018 graphics libraries actually do. Bottom bar uses linear-space interpolation. The difference is sharpest near the middle for high-chroma pairs.
Alpha compositing and halos
Alpha compositing - putting a partially-transparent image over a background - has exactly the same linearity requirement as blending. The Porter-Duff over operator Cout = Ctop · α + Cbg · (1 − α) is correct in linear light. Apply it to encoded values and you get the same too-dark midtones - typically visible as dark halos around the edges of anti-aliased text or premultiplied alpha images.
Bright foreground on dark background - halos at the edges
A bright disk with an anti-aliased edge is composited over a dark background two ways. Linear compositing produces a smooth transition. Encoded compositing produces a thin dark ring at the boundary - the classic "halo" of incorrectly-gamma-aware alpha blending.
Quantization and the bit budget
If you only have 8 bits per channel - 256 levels - you can either spread them evenly across linear light (which wastes most of them on the bright end where the eye can't distinguish neighbors) or spread them evenly across perceptual lightness (which puts more levels in the dark range where the eye is sensitive). Gamma encoding is exactly that re-allocation.
Same bit depth, very different banding behavior
Top bar: 256 levels evenly spaced in linear light, then converted to display. Notice the obvious banding in the shadows - we wasted code values on the bright end. Bottom bar: 256 levels evenly spaced in sRGB-encoded space. The bands shift to where the eye doesn't see them.
The display gamma chain
The full path from file to photon traverses several stages, each of which can introduce or undo a transfer function. Visualising the chain prevents most gamma bugs.
HDR transfer functions: PQ and HLG
Traditional gamma assumes a fixed maximum: file value 1.0 = display white at some small fixed luminance, typically 100 cd/m². HDR breaks that assumption. HDR displays can show peaks of 1000-10 000 cd/m² and need transfer functions designed for that range.
Two HDR transfer functions dominate. PQ (Perceptual Quantizer, SMPTE ST 2084, used in HDR10 and Dolby Vision) maps file values 0-1 to absolute luminance 0-10 000 cd/m² using a curve based on the Barten contrast-sensitivity model. HLG (Hybrid Log-Gamma, used by BBC and NHK for broadcast HDR) is relative - its lower half is a standard gamma curve so legacy SDR receivers decode something usable, and the upper half is logarithmic to extend dynamic range.
Common gamma bugs
Knowing the curve is one thing; spotting where a pipeline is missing it is another. The following bugs are everywhere.
Dark mustard yellows
50/50 mixes of red and green produce a dark olive instead of bright yellow. CSS engines pre-2023 did this in encoded space.
color-mix(in srgb-linear, ...)).Halos around text
Anti-aliased dark text on a bright background shows a thin lighter ring; bright text on dark shows a thin darker ring.
Muddy gradient midpoints
The middle of a blue-to-yellow CSS gradient looks dark gray instead of bright neutral.
linear-gradient(in oklab, ...) or in oklch.Resize artefacts
Downsampled images get darker overall, especially with bright fine detail on dark backgrounds.
Wrong WebGL textures
Textures sampled as if linear when they were authored as sRGB - everything looks too dark.
Wrong shadow brightness
Renderer adds two lights in encoded space - the sum is darker than expected, ambient looks washed out.
Premultiplied alpha edges
PNG with premultiplied alpha shows dark fringes when composited over light backgrounds.
HDR file shown as SDR
An untagged PQ file shown on an SDR pipeline looks nearly black except for the bright peaks.
Test your understanding
Six questions on gamma, linear light, and transfer functions. Wrong answers come with brief explanations.
Quick check
Continue your journey
Color in Film and Video: Log, LUTs, and Grading
Log is a transfer function put to work in the grading suite.
Digital · 35Bit Depth, Channels, and Alpha
The stored numbers these transfer functions encode - and why low bit depth bands.
Digital · 27Color Gradients and Interpolation
The dark-middle blending bug is gamma encoding meeting interpolation.
Foundations · 01What Color Is and How Humans See It
The cornerstone explainer connecting light, surface, eye, brain, and standards.
Physics · 03The Physics of Light, Wavelength, and Spectrum
The photons that gamma encoding ultimately quantifies.
Colorimetry · 08CIE XYZ Explained
The Y coordinate is where linear-light luminance is defined.
Colorimetry · 09CIELAB and LCH Explained
L* is the perceptual cube root that gamma encoding approximates.
Digital · 12RGB, sRGB, Adobe RGB, ProPhoto, Display P3, Rec.2020
The spaces in which each transfer function is defined.
Design · 13Accessible Color Design and WCAG Contrast
WCAG luminance is computed from gamma-decoded linear values.