30

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.

Digital · 14 7 Live Demos ~50 min read Linear ↔ Encoded
~γ 2.2
Effective sRGB
γ 2.4
BT.1886 video
γ 1.8
ProPhoto archive
PQ / HLG
HDR transfer
00

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.

Interactive 00 - Advanced gamma pipeline

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.

Decoded linear -
Correct output -
Encoded-space result -
Error load -
Banding risk -
Pipeline verdict -

Decode before math, do the operation in linear light, then encode for delivery. This panel shows what breaks when that order changes.

01

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.

The bit-budget bonus. By storing more code values in the dark range where the eye is sensitive, gamma encoding produces visually smoother gradients at low bit depths than a linear encoding would. 8-bit sRGB looks acceptable; 8-bit linear bands aggressively in shadows. This is the second reason gamma survives - the first was historical, the second is informational.
02

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.

Interactive 01 · Perception vs linear ramp

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.

Top: linear Y (luminance) · Bottom: linear L* (perceived lightness)
03

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 = 12.92 · vlinear   for vlinear ≤ 0.0031308
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.

The "pow(v, 2.2)" shortcut. A very common implementation mistake is to use a pure 2.2 power instead of the piecewise sRGB curve. The shortcut is fast and looks correct for most values, but introduces visible errors in deep shadows. The differences are usually within ±1 code value at 8 bits, which is tolerable for casual rendering and disastrous for color-critical work.
04

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
Interactive 02 · Transfer curve plotter

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.

sRGB BT.1886 γ 2.4 Adobe γ 2.2 ProPhoto γ 1.8 DCI γ 2.6 Linear PQ HDR HLG HDR
x: encoded 0-1 · y: linear-light 0-1 · PQ and HLG compressed for visualization
05

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.

Encoded RGB
File-stored values. Good for storage (bit-efficient), viewing (matches display response), and direct display output.
Linear RGB
Light-proportional values. Required for blending, compositing, lighting math, matrix transforms, blur, resize, and physically accurate rendering.
Scene-referred
Linear values that track scene luminance. Can exceed 1 for highlights. The space of modern cinema mastering and HDR capture.
Display-referred
Encoded values clamped to 0-1, designed for direct display output. The space of file formats and final delivery.
Half-float linear
16-bit floating-point linear values. The standard intermediate in compositing pipelines because it supports HDR values without quantization issues.
Tonemapping
Compressing scene-referred linear values into a display-referred range. Required when scene luminance exceeds what the display can show.
"Files store encoded. Compositors compute linear. Displays show encoded again. Anything that crosses these boundaries needs an explicit transfer-function conversion." Editorial summary · the linearization principle
06

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.

Interactive 03 · Linear vs encoded blending

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.

#7f7f00
#b8b800
Encoded-space blend (wrong)
Naive average of (255,0,0) and (0,255,0) → #7f7f00 - looks darker than either source.
Linear-space blend (correct)
Decode, average, re-encode → #b8b800 - bright midpoint as expected.
Interactive 04 · Gradient comparison

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.

Top: encoded interpolation · Bottom: linear interpolation
07

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.

Interactive 05 · Alpha compositing halos

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.

Encoded (wrong - halos)
Linear (correct - clean edges)
Premultiplied alpha requires linear-light multiplication as well. A common bug: store premultiplied alpha in an encoded sRGB texture, then divide out alpha in encoded space at sample time. The result is dark mid-edges. Premultiplication must happen in linear; the storage format must either be linear or the runtime must un-encode → multiply → re-encode.
08

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.

Interactive 06 · 8-bit linear vs 8-bit encoded

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.

Top: linear quantization · Bottom: sRGB-encoded quantization · same total levels
8-bit sRGB
~ 11 stops usable
8-bit linear
~ 7-8 stops usable
10-bit sRGB
No visible banding
16-bit linear
Editing intermediate
09

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.

1. File on disk
Encoded RGB. Format-dependent: JPEG and PNG default to sRGB; AVIF / HEIF / JPEG XL carry explicit transfer tags; raw files store scene-linear sensor data.
2. Decoder
Reads file values, applies the inverse transfer function, produces linear-light pixel buffers for image processing.
3. Compositor
Blends, blurs, transforms in linear space. Modern browsers, GPU compositors, and game engines do this; many CSS implementations still don't.
4. Encoder for output
Re-applies the destination transfer function. For sRGB displays, this restores the encoded form for transmission.
5. Display electronics
LCD/OLED panels accept encoded values and apply their own electrical-to-optical transfer curve. Most simulate the historic CRT 2.2-gamma response.
6. Viewer eye
Receives linear-light photons but processes them non-linearly through the cube-root lightness response - which gamma encoding implicitly matches.
The simplification. If your software produces correct encoded output for the destination, all the rest of the chain works automatically. The discipline is to keep transfer-function conversions explicit at every boundary, not to fight the chain at every step.
10

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.

PQ (BT.2100)
Absolute luminance 0-10000 cd/m². Used in HDR10, Dolby Vision, most streaming HDR. Not backward-compatible with SDR.
HLG (BT.2100)
Relative range. Lower half is BT.709-compatible gamma; upper half extends dynamically. Used in broadcast HDR.
10-12 bit depth required
8-bit HDR doesn't work - the wider luminance range needs more code values. 10-bit minimum, 12-bit common in mastering.
Tone mapping
Adapts an HDR signal to a lower-peak display. Standard in HDR-capable displays whose actual peak is below the encoded peak.
HDR is not just brighter SDR. The transfer function changes meaning: PQ encodes absolute physical luminance, not "fraction of display max." An incorrectly tagged file (PQ-as-HLG or vice versa) decodes to wildly wrong values. Tagging matters more in HDR than it ever did in SDR.
11

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.

Fix: blend in linear (CSS Color 4 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.

Fix: anti-alias in linear light; modern font renderers do.

Muddy gradient midpoints

The middle of a blue-to-yellow CSS gradient looks dark gray instead of bright neutral.

Fix: use linear-gradient(in oklab, ...) or in oklch.

Resize artefacts

Downsampled images get darker overall, especially with bright fine detail on dark backgrounds.

Fix: resize in linear space, then re-encode.

Wrong WebGL textures

Textures sampled as if linear when they were authored as sRGB - everything looks too dark.

Fix: use SRGB texture formats so the GPU decodes on sample.

Wrong shadow brightness

Renderer adds two lights in encoded space - the sum is darker than expected, ambient looks washed out.

Fix: lighting is physical; do it in linear, convert to encoded only at output.

Premultiplied alpha edges

PNG with premultiplied alpha shows dark fringes when composited over light backgrounds.

Fix: premultiply in linear space, or use straight alpha.

HDR file shown as SDR

An untagged PQ file shown on an SDR pipeline looks nearly black except for the bright peaks.

Fix: never strip transfer-function metadata; tone-map at the boundary.
12

Test your understanding

Six questions on gamma, linear light, and transfer functions. Wrong answers come with brief explanations.

Quick check

Loading…
 
Question 1 of 6
13

Continue your journey