#### <sup>[Ollin](../../index.md) → [Documentation](../index.md) → [Concepts](index.md) → `Light and color`</sup>

---

## Light and color

A `Color` you write is an sRGB color, which is the kind of number a display takes. In the middle of a frame, Ollin works in linear light instead, where a number is proportional to the light it stands for. The last pass of the frame converts back and puts the picture on screen.

You can write sketches for a long time without thinking about any of this. It is still worth one screen of reading, because it explains why a few things behave better here than they do elsewhere.

### Why the middle is linear

sRGB numbers are not proportional to light. Mid-gray, 128 of 255, carries about a fifth of the light of white rather than half of it. So adding or averaging numbers like that directly gives the wrong answer. Two lights sum too bright, a blur comes out darker than what went in, and a half-covered edge reads too dark.

Shaders therefore convert the colors you give them into linear light, and every shape composites into a floating-point canvas that holds light the same way. That canvas can hold values above 1, because light adds up and a bright thing is allowed to be bright.

### What the last pass does

Once a frame is finished, one pass turns that canvas into the image on screen. The pass does three things, in this order:

1. **Tone-maps.** Values above 1 roll off toward white instead of being cut flat. `toneMap(_:)` chooses the shape of that roll.
2. **Dithers.** A pattern fixed to the pixel position breaks up the step between neighboring output levels, so a smooth ramp does not band. The pattern moves a value by at most one level, and less within a level of black or white, so it never pushes past either end. Exact black stays 0 and exact white stays 255, so a picture of only black and white exports as only black and white.
3. **Encodes to sRGB** and writes to the display.

That is the only point where a frame is reduced to eight bits per channel. Exports run the same pass, which is why an exported file matches the window.

### What follows from it

- **Adding light works.** `blendMode(.add)` with faint marks sums the way light sums. That is what makes an [accumulated](../drawing/accumulation.md) canvas read as a long exposure instead of a smear.
- **Blur and bloom keep their brightness.** A blurred layer is blurred in linear light, so it does not darken.
- **Bright can stay bright.** On a display with headroom, [`colorOutput`](../drawing/coloroutput.md) presents through Display P3. Highlights then go past white instead of clipping at it.
- **A clear color is a color too.** The background is converted like everything else. So the frame starts from the same light that the shapes composite into.
- **You can name a color by its light.** `Color(linear:green:blue:)` takes linear numbers and stores the sRGB color that means them, and `linearRGB` reads a color back as light. A shade worked out in light, or a `SprayLine`'s `light`, never has to pass through the curve by hand.

There is one deliberate exception. Ollin remaps the *coverage* of a thin stroke or a small dot, so that a fine mark does not read faint or beaded. The remap touches partial coverage only, never a color or an alpha you set.

### Read next

- [`HDR & tone mapping`](../drawing/hdr.md) - the float pipeline and the tone-map curves, with the looks they make.
- [`Color`](../drawing/color.md) - the `Color` type, OKLab mixing, ramps, palettes, and colormaps.
- [`Wide gamut & HDR output`](../drawing/coloroutput.md) - P3 on screen, highlights past white, HDR video and stills.
- [Guide, Chapter 2](../../guide/02-color.md) - color taught as a chapter, with the same idea in pictures.
