Guides · Color

Color, From Light to Pixels!

How ACES works (and why it matters). A field guide to the color pipeline - written for artists, not color scientists.

~20 min read · 10 interactive widgets

Five renders. Same scene, same lights, same geometry. Four of them are wrong, and if you can’t tell which, you’ve been shipping wrong color your whole career. Here’s how to see it.

Hover or tap an image to identify the colorspace error.

Five renders. Same scene, same lights, same geometry. Four are wrong. Hover or tap to see how each one is wrong.

Color management is invisible when it’s right and obvious when it’s wrong, but most of the time it’s wrong in ways that are almost invisible - a slightly-too-warm sky, a black that isn’t quite black, a green that’s been poisoned in conversion. This page is about the system I reach for to keep that from happening. It’s called ACES, and once you understand it, you can’t unsee it.

Before we can talk about ACES, we have to talk about light.

How light becomes pixels

Light is physical. Pixels are not.

A real-world scene has effectively unlimited dynamic range. The sun is millions of times brighter than a shadow. A camera sensor counts photons (more or less), and the numbers it produces are linear with respect to light: twice the photons, twice the value.

A monitor emits light in a different way, and human vision perceives that light non-linearly. Somewhere between the sensor and your eyeball, those linear photon counts have to become the right pixel values for your monitor. That somewhere is the color pipeline.

The four transforms

Every shot goes through (at least) four transforms:

  1. Camera encoding. The camera squeezes its high dynamic range into a compact format (Log3G10 for RED, Log-C for ARRI, etc.).

  2. Working space conversion. That camera format gets converted to a wide-gamut linear space for VFX work.

  3. Grade space conversion. For grading, we move from linear to a log-encoded space that’s intuitive to artists.

  4. Output transform. Finally, everything gets compressed and shaped to fit a Rec.709 monitor or an sRGB browser.

Camera → working → grading → delivery. Click a stage to see what actually happens at that step. The chain is reversible up to the ODT; after that, you've baked in a specific display.

Why each transform exists

The camera transform exists because the sensor’s full range can’t fit in a normal file format without compression. The working-space transform exists because all light-based math needs to happen in linear values. The grade transform exists because grading curves only behave the way artists expect when stops of light are evenly spaced. And the output transform exists because a Rec.709 monitor cannot show what’s in the working file - it has to be told what to show.

Each of those steps has rules. To follow them, you need to know what a color space actually is - not the marketing version, the working version.

The three things a color space actually is

A color space is a set of rules that maps numbers to colors. Three components define it: a gamut (which colors can be represented), a transfer function (how brightness is encoded), and a white point (what counts as neutral).

Gamut, primaries, and the horseshoe

The CIE 1931 horseshoe is the map of all colors humans can see. A gamut is a triangle on that map - the corners are the red, green, and blue primaries of the color space. Inside the triangle, you can mix the primaries to make any color. Outside the triangle, your color space simply can’t represent it.

CIE 1931 chromaticity diagram (horseshoe locus).

Toggle a gamut to see its triangle. The corners are the red, green, and blue primaries; the dot is the white point.

Each triangle is the set of colors a working space can represent. AP0 (ACES2065-1) extends outside the visible spectrum on purpose - archival-only. ACEScg's AP1 stays inside, wide but well-behaved.

Notice what happens when you turn on ACES2065-1: the triangle extends outside the horseshoe. That’s not a bug. AP0

  • the primaries used by ACES2065-1 - was chosen to contain every visible color, including ones that don’t physically exist. This makes it useless for daily work and perfect for archival.

ACEScg, by contrast, uses AP1 primaries that fit just inside the visible spectrum. Wide enough to handle anything a real camera can capture, narrow enough to behave well in math. This is why we render in ACEScg, not AP0.

Transfer function (gamma)

The eye doesn’t see light linearly. Doubling the photons doesn’t double the perceived brightness. Display systems and image formats take advantage of this with a transfer function - a curve that allocates more code values to dark tones (where we’re sensitive) and fewer to bright tones (where we’re not). The curve has a name in shorthand: gamma. A gamma of 1.0 is linear. A gamma of 2.2 is the rough perceptual curve used by sRGB.

Curve
Linear input Encoded value 0 0.5 1.0 0 0.5 1.0
Gamma
gamma = 2.20

The curve is y = x^(1/gamma). Real sRGB uses a piecewise function with a small linear toe near zero, but the pure power curve is close enough to see the idea.

Result
LinearEncoded (8-bit)
0.0-
0.2-
0.4-
0.6-
0.8-
1.0-
A pure power curve maps linear scene values to encoded display values. Drag the slider to change gamma; the curve, the gradient, and the 8-bit code values all redistribute together.

Drag the slider to 1.0 - that’s linear, no curve. Drag it to 2.2 - sRGB. Drag it to 2.4 - Rec.709 broadcast. Notice how the gradient bar’s midtones get darker as gamma increases, even though the math is just output = input^(1/gamma). That’s the entire concept. Everything else is engineering details around that curve.

White point

The third leg of the stool. The white point is the chromaticity that a color space considers neutral - what counts as “white”. D65 (≈6500 K) dominates web and broadcast because it approximates average daylight. Cinema uses DCI white, slightly green of D65, for projector standardization. ACES uses D60. Mismatched white points produce subtle casts in supposedly neutral areas - gray cards drift, skin tones shift. It’s the kind of error you can stare at for an hour without naming.

So a color space is a triangle (gamut), a curve (transfer function), and a reference white. Once you have those three, you have everything. Now let’s talk about why we use different color spaces for different stages of the pipeline.

Linear vs log, and why we have both

Linear is for math

Light behaves linearly. Two lights at brightness 0.5 add up to brightness 1.0. Glows, motion blur, depth of field, exposure adjustments - these all need real-light math. If you do them on gamma-encoded values, the math is wrong, and you get the kinds of artifacts everyone has seen but few have named: glows that look anemic, blurs that smear toward white, exposure changes that crush shadows wrong.

Log is for grading

The problem with linear: it allocates almost all its bit budget to a tiny range of bright values, because the dynamic range of a real scene runs from ~0.001 to ~1000+ in scene-linear units. Log spaces redistribute that range so each stop of light gets roughly equal precision. That makes grading intuitive: when you push the shadow region of a curve, the curve actually moves the shadow region. In linear, the shadow region is squeezed into the bottom 1% of your data and curves are unworkable.

EV = +0.00
Linear encoding · ACEScg scene-linear

Midgray (0.18) lands at 18% of full white. Almost the entire bit range is allocated above midgray.

Log encoding · ACEScct

Midgray sits at code value 0.413. Each stop of light gets roughly equal precision - what you want under your hands when grading.

The same 14-stop scene rendered as a histogram of bit allocation. Linear bunches almost everything into the brightest stop; ACEScct spreads it evenly. Drag exposure to shift the viewing window.

The top histogram shows linear bit allocation. Look at where midgray (0.18) lands - it’s only 18% of the way up the chart, but visually it’s the center of a normal exposure. Now look at the bottom histogram, which is ACEScct (log). Midgray is at code value 0.413, and the stops are evenly spaced. If you’re pulling a grade with curves and lift/gamma/gain controls, log is what you want under your hands.

So we use both

Comp in ACEScg (linear). Grade in ACEScct (log). Convert between them losslessly via OCIO. Each space is doing the job it’s good at. The conversion is not lossy, not creative - it’s just a different encoding of the same scene.

Bit allocation matters in another way too: how many total bits you have to spend.

Bit depth and why floats matter

What bit depth actually buys you

  • 8 bits per channel: 256 values. Fine for delivery, terrible for working.
  • 10 bits: 1024 values. Better. Standard for video.
  • 16-bit float: enormous range, including values above 1.0 (highlights brighter than white). Standard for VFX.

The gamma test

8-bit · 256 levels
10-bit · 1024 levels
16-bit float · effectively continuous
γ = 2.20

At γ = 2.20 (sRGB display), 8-bit shadows are starting to band.

A clean linear black-to-white ramp, quantized at three precisions and viewed through a display gamma curve. At γ = 1.0 the three look identical (all bits used evenly). As γ climbs - sRGB is 2.2, Rec.709 is 2.4, log curves go higher still - the dark end gets stretched and the bit-depth quantization that was hidden in shadow shows up as bands. 8-bit gives out first, then 10-bit; float stays smooth because there is no quantization to expose.

Drag the gamma slider. At γ = 1.0 the three look identical - every bit is used evenly across the ramp. Push past γ = 2.2 (sRGB display) and the 8-bit version starts showing visible stair-steps in the shadows. By γ = 3.0 the bands are unmistakable in 8-bit; 10-bit is starting to show. The float version stays smooth, because there’s effectively no quantization to expose.

This is why we work in 16-bit float EXR for VFX. Not because we need the precision today, but because we don’t know what we’re going to do to the image tomorrow. Future-you will push that gradient through three more curves and a defocus, and 8-bit you would have a banded mess.

Now we have the pieces. Let’s see how they fit together end-to-end.

The ACES pipeline

Camera → working → grading → delivery. Five color spaces, five conversions, no surprises if everything is set up correctly. Click any stage to see what happens at that stage in detail.

Camera → working → grading → delivery. Click a stage to see what actually happens at that step. The chain is reversible up to the ODT; after that, you've baked in a specific display.

Conversions are reversible (mostly)

Every conversion in this chain is mathematically defined and reversible. ACEScg ↔ ACEScct is a clean round-trip. Log3G10 → ACEScg is clean. ACES2065-1 → ACEScg → ACES2065-1 is clean.

Where you lose data: at the ODT, by design. The ODT is creatively compressing your wide-gamut, high-dynamic-range work into something a Rec.709 monitor can show. That’s a one-way process for any single delivery, but the master in ACEScg is preserved

  • re-master to HDR later without re-rendering anything.

Speaking of the ODT - let’s see what one actually does to an image.

The ODT - your monitor is lying to you (on purpose)

What you’ve never been seeing

Every image in Nuke, Resolve, After Effects (when configured correctly), and any OCIO-aware viewer is being shown to you through a viewing transform. You’ve never seen the raw scene-referred values. They would look terrible.

The reveal

Drag the handle to compare.
The ODT (Output Device Transform) is what your viewer applies on the fly to convert scene-linear values into something a Rec.709 monitor can display. The right side is what you've been calling "the image" your whole career. The left side is what's actually in the file.

The left side is the raw scene-referred ACEScg values, displayed as if they were sRGB - milky, low-contrast, washed out. The right side is the same data through the Rec.709 ODT - punchy, saturated, normal-looking.

The right side is what you’ve been calling “the image” your whole career. The left side is what’s actually in the file. The ODT is the function that turns one into the other, and it does it on the fly, every time you look at your viewport.

Why this matters

  • The ODT does not affect your render or export (unless you bake it in intentionally). It’s a viewing-only operation.

  • Different ODTs exist for different displays: Rec.709, sRGB, DCI-P3 cinema, Rec.2020 HDR.

  • If you grade or composite while looking through the wrong ODT, your work will look wrong on the right one.

The ODT is one source of “looks wrong” errors. Let’s look at the others.

Common failure modes

Almost everyone has shipped at least one of these errors. They’re the failure modes that come from pipeline mistakes, asset mismanagement, or just being in the wrong color space at the wrong moment. The good news is that once you’ve seen the type of error, you can recognize it forever.

1 / 5  ·  0 correct
Diagnose: identify the colorspace error in this image. wrong

The reds are neon, the blues are electric. The render math otherwise looks correct.

    Each image is a real failure mode. Pick the diagnosis. The right answer reveals the corrected version of the same scene so you can see the pattern.

    Patterns to internalize

    • Crushed shadows + saturated highlights → color space mismatch on input.
    • Washed-out, low-contrast everything → ODT missing or wrong.
    • Banded gradients → bit depth too low, or color space conversion routed through 8-bit.
    • Color casts on neutrals → white point mismatch.
    • Highlights that go magenta → out-of-gamut handling failure.

    If you remember nothing else from this page, remember the rules.

    Reference card

    If you’ve made it this far, you’ve got the concepts. Here are the rules.

    ACES color rules - at a glance

    Where does color start?
    • RED footage → Log3G10 / RedWideGamut
    • ARRI footage → Log-C / AWG
    • sRGB textures, stills → sRGB (convert in)
    • Render output → ACEScg EXR
    Where does work happen?
    • Lighting & rendering → ACEScg (linear, AP1)
    • Compositing → ACEScg
    • Grading → ACEScct (log)
    • Working bit depth → 16-bit float EXR
    Where does color end?
    • Broadcast master → Rec.709 (γ 2.4)
    • Web master → sRGB (γ ≈ 2.2)
    • Cinema (DCP) → DCI-P3 (γ 2.6)
    • HDR re-master → Rec.2020 + PQ/HLG (from ACEScg)
    Three columns. Twelve lines. Pin it above the workstation.

    The “Print this card” button strips everything else and prints just the card. Pin it. Share it.

    Glossary

    Every term used in this page, defined once. In the prose, underlined-dotted terms show their short definition on hover or focus.

    ACES
    A set of color spaces, transforms, and a reference rendering / output transform stack maintained by the Academy. The umbrella under which ACEScg, ACEScct, and ACES2065-1 live.
    ACEScg
    Scene-linear, wide-gamut working color space using AP1 primaries. The space we render and composite in.
    ACEScct
    A logarithmic encoding similar in feel to Log-C, used for grading. AP1 primaries, evenly-spaced stops, lossless round-trip with ACEScg.
    ACES2065-1
    The interchange/archival space defined by ACES, using AP0 primaries that span the entire visible spectrum (and beyond). Not for daily work - its triangle includes "impossible" colors.
    AP0
    The triangle of primaries used by ACES2065-1. Chosen to enclose every visible color, even at the cost of including coordinates outside the visible spectrum.
    AP1
    A narrower triangle that fits inside the visible spectrum but is still wider than Rec.2020. Used by ACEScg and ACEScct.
    Bit depth
    How many discrete values each color channel can take. 8-bit gives 256 values; 10-bit gives 1024; 16-bit float is effectively continuous and supports values above 1.0.
    Banding
    Visible jumps between adjacent color values in what should be a smooth gradient. Almost always a bit-depth or quantization symptom.
    CIE 1931
    A standard 2D map (xy chromaticity) of all colors humans can perceive, derived from CIE 1931 color-matching experiments. The "horseshoe" outline is the spectrum locus.
    Chromaticity
    A coordinate (typically xy) that describes the hue and saturation of a color independent of its brightness.
    Color primary
    One of the three reference colors that define the corners of a color space's gamut triangle. Mixing primaries produces every color the space can represent.
    Color space
    A set of rules mapping numbers to colors. Three components: a gamut (which colors), a transfer function (how brightness is encoded), and a white point (what counts as neutral).
    Gamut
    The triangle of colors a color space can represent. Defined by its three primaries.
    D65
    A standard illuminant approximating average daylight. The default white point for sRGB, Rec.709, and Rec.2020.
    DCI white
    The white point used by the DCI-P3 standard for cinema projection. Slightly greener than D65.
    White point
    The chromaticity that the color space considers "white" or neutral. Mismatched white points produce subtle color casts in supposedly neutral areas.
    DCI-P3
    A wide gamut used for digital cinema projection. Wider than Rec.709, narrower than Rec.2020. Uses DCI white.
    Display P3
    P3 primaries with the D65 white point. Used by modern Apple displays. Common destination for finishing on-set.
    Display-referred
    Color values that have been processed (through an ODT) so that displaying them on the target device produces the intended look. Opposite of scene-referred.
    Scene-referred
    Color values that represent physical light in the scene, before any display-shaping transform. Linear, often very high dynamic range.
    EXR
    OpenEXR - a high-dynamic-range image format that supports 16-bit and 32-bit float per channel. The default format for VFX pipelines.
    Gamma
    Shorthand for the power-curve transfer function. <code>output = input^(1/gamma)</code>. sRGB is roughly gamma 2.2; Rec.709 is roughly 2.4.
    Transfer function
    The mathematical curve that converts linear scene values to encoded values, and vice versa. The "shape" of a color space's tone response.
    LUT
    A table of input → output color values used to apply a complex transform without recomputing it. Comes in 1D and 3D varieties.
    1D LUT
    A LUT that operates on each channel independently. Good for tone curves; cannot move chromaticity.
    3D LUT
    A LUT indexed by RGB triplets. Can express any RGB → RGB transform, including hue/saturation manipulations.
    IDT
    The transform that takes raw camera or asset data into ACES working space. The first conversion in the pipeline.
    ODT
    The transform that shapes ACES working color into something a specific display device can show. By design, this is a creative compression - not reversible.
    RRT
    The "look" applied between ACES working space and the ODT. Provides ACES's baseline tonal character regardless of which display ODT is downstream.
    Linear
    A transfer function where the encoded value is proportional to scene light. Doubling the value doubles the photons. Required for correct light math.
    Log
    A logarithmic transfer function. Each stop of light gets roughly equal precision, which makes grading curves intuitive.
    Log3G10
    RED's logarithmic encoding for footage. Captures the camera's full dynamic range in a compact form. Must be converted to linear before compositing math.
    Log-C
    ARRI's logarithmic encoding for footage. Like Log3G10, must be converted to linear for math.
    S-Log
    Sony's logarithmic encoding family (S-Log2, S-Log3). Same purpose: compact storage of high dynamic range.
    OCIO
    An open-source color management library used across DCCs (Nuke, Resolve, Maya, Blender, etc.) to apply consistent color transforms via shared config files.
    Rec.709
    The color space used by most HD broadcast and SDR video. Same primaries as sRGB, slightly different gamma (~2.4 vs ~2.2).
    Rec.2020
    A wider color space defined for UHD and HDR broadcast. Wider than DCI-P3, narrower than ACEScg.
    sRGB
    The default color space for the web and consumer displays. Same primaries as Rec.709 with a piecewise transfer function (~gamma 2.2).
    Working space
    The color space in which a particular task is performed (ACEScg for render/comp, ACEScct for grading). Distinct from input or display spaces.
    Display space
    The color space the final viewing device expects (Rec.709 for broadcast, sRGB for web, etc.).

    The pipeline is invisible when it works. Now you know what to look for when it doesn’t.