v155 · canvas · color
PredefinedColorSpace srgb-linear and display-p3-linear
The 2D canvas has had colorSpace: "srgb" | "display-p3" since Chrome 94. Chrome 155 extends the PredefinedColorSpace enum with the two linear-light variants CSS already defines — "srgb-linear" and "display-p3-linear" — so a canvas backing store, and an ImageData, can hold pixel values that are proportional to light. That is the space where gradients stop going muddy in the middle and where adding two lights actually adds their photons.
concepts
-
Context settings
The canonical surface: pass each of the four enum values to
getContext("2d", { colorSpace }), read the real answer back withgetContextAttributes(), and see exactly what an unsupported value does — it throws, it is not ignored. Live matrix, verbatim results. -
ImageData round trip
new ImageData(w, h, { colorSpace })tags pixels with a space, andgetImageData()converts between spaces on request. Pick a colour, push it through every space the browser has, and compare the raw bytes that come back. -
Linear-light gradients
Why linear matters: the same two gradient stops interpolated in gamma-encoded sRGB versus linear light, side by side, with the midpoint pixel sampled from each canvas. The gamma version dips dark and grey in the middle; the linear one keeps its luminance.
-
Additive spotlights
Three coloured lights composited with
globalCompositeOperation: "lighter". In a gamma-encoded canvas, "lighter" adds encoded bytes — physically meaningless. In a linear canvas the addition models real light. Move the lights and watch the overlaps disagree. -
Detection traps
How to feature-detect this correctly — and the two wrong ways that look right. Unknown enum values throw
TypeError; unknown dictionary keys are silently ignored; a probe that cannot tell those apart reports support that is not there.
why it shipped
Pixel values in an ordinary canvas are gamma-encoded: the byte 128 is not half the light of 255, it is about 22% of it. That encoding is perceptually efficient for storage, but every arithmetic operation the canvas performs — gradient interpolation, image smoothing, compositing, "lighter" — happens on the encoded values, which is mathematically the wrong domain for anything that models light. Games, renderers, anti-aliasing and image-resampling pipelines all work in linear light for exactly this reason, and until now the only way to do that on the web was to run your own conversion in JavaScript or move to WebGL/WebGPU.
CSS Color 4 already defines srgb-linear and display-p3-linear as predefined colour spaces; Chrome 155 lets a canvas backing store use them directly, so the browser does its blending in the right domain and converts to the display at the end — the same conversion an <img> with a matching colour profile gets.
Availability: this shipped to stable in Chrome 155. On earlier versions the runtime flag is ColorSpacePredefinedLinearSpaces (status: experimental) — launch with --enable-blink-features=ColorSpacePredefinedLinearSpaces or --enable-experimental-web-platform-features. There is no dedicated chrome://flags entry. Every demo here probes the exact enum values and shows the browser's real answer either way.