v155 · canvas color spaces

Additive spotlights

globalCompositeOperation: "lighter" literally adds pixel values. In the default canvas those values are gamma-encoded, so "lighter" adds numbers that are not light — overlaps oversaturate and clip in ways no physical lamp would. In an srgb-linear canvas the addition is photon arithmetic: two half-intensity lights make one full-intensity patch, exactly.

Ships in Chrome 155 (--enable-blink-features=ColorSpacePredefinedLinearSpaces on earlier builds). Where the linear store is unavailable, the second panel is a labelled JavaScript reference rendering of the same additive model computed in linear light — the panel's badge says which you are seeing, and it is never presented as native output.

Three lights, one addition, two number systems

Gamma-encoded store: "lighter" adds encoded bytes

native canvas

Linear-light store: "lighter" adds light

checking…

the API

const ctx = canvas.getContext("2d", { colorSpace: "srgb-linear" });
ctx.globalCompositeOperation = "lighter";   // additive
for (const light of lights) {
  const g = ctx.createRadialGradient(light.x, y, 0, light.x, y, r);
  g.addColorStop(0, light.color);
  g.addColorStop(1, "transparent");
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, w, h);
}
// In a linear store this models real additive light.
// In the default gamma store the same code adds encoded values instead.

see also