Glance at the summary firstThe summary from the end of the lesson, as a road map before you start reading.
- Dynamic color extracts a single source color from the wallpaper or a color the user chooses; scoring eliminates nearly gray colors and very small areas, ranks the rest by the balance of area and chroma, and falls back to a fixed color if there's no suitable one.
- The palette is derived in the HCT space from the source color's hue, using preset chroma and lightness steps; because lightness is directly L*, contrast can be predicted independently of the source color, and light and dark skin tones within the same hue family produce the same interface color.
- The brand's cost is that the accent color passes to the user and vivid brand colors get muted; teams choose between rejecting dynamic color, accepting it only on neutral surfaces, harmonizing their own colors by at most fifteen degrees, or moving recognition to non-color signals.
Short answer
Dynamic color systems extract a single source color from the user's wallpaper or a color the user chooses, and derive the entire interface's palette from that color's hue, with lightness and saturation following preset steps; as a result, every user's phone takes on different colors, but the contrast between text and background stays the same regardless of the source. The cost to the brand is clear: the app's accent color is now the user's choice, not the brand's, and a saturated brand color can become unrecognizable once it's pulled toward the system's own saturation limit. Brands therefore either accept dynamic color only on some surfaces, or adjust their own colors slightly toward the system's color, or move their recognizability to non-color signals like the logo, shape, and typeface.
From wallpaper to interface
In UX-01, we split a color system into layers: the raw palette, role names that carry meaning, and components. That lesson's assumption was that the design team chose the raw palette. Dynamic color breaks this assumption: the raw palette is generated at runtime from a photo on the user's device.
Android's developer documentation describes the feature this way: dynamic color, added in Android 12, lets users make their devices tonally consistent with the color scheme of their personal wallpaper or a color they choose in the wallpaper picker. The documentation says apps can apply this theme to their own screens with an interface call; in other words, dynamic color is optional for an app, not mandatory. According to the same page, the first release had only one variant, tonal spot; Android 13 added other variants.
Google's open-source Material Color Utilities library runs this system. The library splits the same job into several parts: quantization, which reduces an image to a small number of colors; scoring, which ranks these colors by how suitable they are for a theme; a palette component, which generates tonal palettes from the chosen color; and a scheme component, which binds the palettes to roles. Every number in this lesson was either read from the library's own source code or calculated by running the library.
How the source color is chosen
A wallpaper contains thousands of colors. The library first quantizes the image: it clusters similar pixels down to a few dozen representative colors and counts how much space each color occupies in the image. It then scores these colors.
Reading the scoring code shows clearly what the system considers a "good source color." Colors with a chroma below 5 — in other words, almost gray — are eliminated from the start. Hues that, together with their surroundings on the hue wheel, cover less than one percent of the image are also eliminated. The remaining colors' score is made up of two components: the area they cover, weighted at seventy percent, and how far their chroma is from a target value defined as 48. The library returns four candidates by default; a note in the code says this matches the number of options in Android's wallpaper picker. If there's no suitable color — for example, if the wallpaper is entirely gray — the library falls back to a fixed color, #4285F4, which it calls Google blue.
One consequence of these rules is surprising. In a test run with the library, a portrait was given that was sixty percent a dark gray background, twenty-five percent a face in the shade #8D5524, and fifteen percent a red scarf in the shade #B0302C. The gray background was eliminated because its chroma was low. Even though the face covered more area than the scarf, the scarf's red took first place, because its chroma was higher. In other words, the photo's "subject" and the interface's color are not the same thing: the system doesn't look for the color that covers the most space, but for the balance between area and vividness.
A whole palette from a single tone
Once the source color is chosen, the system keeps that color's hue but discards its lightness and, in most cases, its saturation. The reason is that the palette's contrast shouldn't depend on the source color's lightness.
We saw, in UX-01's tonal scales, why it matters for a design system to measure lightness along a perceptually arranged axis; the OKLab and OKLCH family is the subject of BI-24, and CIELAB itself is the subject of BI-19. HCT applies the same idea differently: you can move a color's lightness to a desired step without changing its hue, and every color at that step, whatever hue it comes from, gives roughly the same contrast against white.
The library's tonal spot scheme does this job with fixed numbers. The primary palette takes the source color's hue but fixes its chroma at 36. The secondary palette is built at the same hue with a chroma of 16, and the tertiary palette shifts the hue by 60 degrees with a chroma of 24; the neutral palettes use the same hue at chromas of 6 and 8, giving surfaces that are nearly gray but not quite. In light theme, the primary role is bound to this palette's step at a lightness of 40.
The result is easy to see with a calculation. In UX-04, we calculated that a white label on Material's base theme reads at a ratio of 6.4 to one against the #6750A4 background. The tonal spot scheme produces the primary color #89511F from the #8D5524 source; the white label's ratio comes out at the same number here too. Both sit at a lightness of 40. Whatever the source color is, the primary button's readability stays the same; a designer can guarantee the contrast in advance, even for a wallpaper they've never seen.
One part of the library also corrects certain colors on its own. The component called "Dislike" treats dark yellow-greens — hues between 90 and 111 degrees, with a chroma above 16 and a lightness below 65 — as "disliked" and corrects them by lightening them. The code's description says color-preference research shows a universal dislike of dark yellow-greens, and it cites Palmer and Schloss's 2010 study. This is a design decision and it rests on research, but being written into a library means a color from the user's wallpaper gets silently changed.
The brand's loss of control
The question on the lesson card is here: how does dynamic color affect brand recognition?
Many brands use their color as part of their recognition. Dynamic color weakens this signal in two ways. The direct way is this: if an app accepts dynamic color, its buttons and accents come from the user's wallpaper; the same app looks blue on one phone and orange on another. The subtler way is this: even when the brand's own color is given as the source color, the system pulls it into its own rules. In a test run with the library, a vivid red with a chroma of about 102, #E30A17, was brought down to a chroma of 36 once it passed through the tonal spot scheme, and the primary color became a muted, brick-toned red like #904A43. The library's "fidelity" scheme, which tries to preserve the source color's chroma, produced #B6000E from the same red; this is much closer to the brand's red.
Brands have a few answers to this conflict, and none of them is the single right one.
The simplest answer is not to accept dynamic color at all: the app uses its own fixed palette. Recognition is preserved, but the app looks disconnected from the rest of the device.
A middle solution is to accept dynamic color only on neutral surfaces: backgrounds and cards come from the user's palette, while primary buttons and the logo keep the brand's color.
A subtler solution is to harmonize the brand's or status colors slightly toward the system.
Material Color Utilities does this within a defined limit: the fixed color's hue shifts by half of its hue difference from the source color, up to a maximum of fifteen degrees; chroma and lightness are preserved. This way, an error red becomes slightly warmer next to an orange wallpaper and slightly cooler next to a blue one, but it never crosses into a different color family.
The last route is to move recognition to signals other than color. The logo, icon shape, typeface, and the interface's own distinctive layout carry the brand even when color is left up to the user. This is the brand-scale counterpart of the warning made about status colors in UX-02: loading a piece of information onto color alone means losing that information when the color changes. Which route to choose depends on the team's priorities; what's measurable is whether the brand is still recognized in user testing under the route chosen.
Example — a family photo as wallpaper
A user sets a family photo as their phone's wallpaper. The photo has several people in it, and the faces cover a significant part of the image.
A calculation run with the library first shows that human skin tones' HCT hues sit very close to each other: the four skin-tone samples used in the test, #F1C7A9, #C68863, #8D5524, and #4A2C1D, have hues between 44 and 58 degrees, that is, in the orange range; their lightness values, though, range from 21 to 83. The tonal spot scheme discards this lightness: the lightest skin sample, #F1C7A9, and #8D5524, whose lightness is about half of that, produce the same primary color, #89511F; #C68863 and #4A2C1D give brown tones very close to it as well. In other words, if faces become the source color, the interface takes on the same warm brown whether the people in the photo have light or dark skin, and the primary button's contrast stays the same in every case.
This is a fair outcome, and it comes from the design itself: if contrast depended on the source color's lightness, a photo of a dark-skinned family could produce a darker, perhaps less readable interface. But the same calculation also shows a limit. If the photo has a small element with high chroma — say, one of the children's red sweater — the source color could end up being that; what the user expects as "my family's colors" and what the system picks then diverge. This is why Android lets the user choose among several candidates extracted from the wallpaper. A team adding dynamic color to their own app should also test source-color selection and the resulting themes with a photo set that includes people of different skin tones and clothes and backgrounds of different colors.
Common misconception
Common mistake"Dynamic color takes the most dominant color in the wallpaper."
Scoring looks at chroma along with area and eliminates colors that are nearly gray; a small but vivid part of the image can outrank a large but dull area.
Common mistake"If I give the brand color as the source color, the app will appear in the brand's color."
The tonal spot scheme keeps the source's hue but fixes its chroma at 36; a vivid brand color gets noticeably muted this way. Schemes that try to preserve the source color's chroma, or other approaches like color harmonization, are needed instead.
Summary
- Dynamic color extracts a single source color from the wallpaper or a color the user chooses; scoring eliminates nearly gray colors and very small areas, ranks the rest by the balance of area and chroma, and falls back to a fixed color if there's no suitable one.
- The palette is derived in the HCT space from the source color's hue, using preset chroma and lightness steps; because lightness is directly L*, contrast can be predicted independently of the source color, and light and dark skin tones within the same hue family produce the same interface color.
- The brand's cost is that the accent color passes to the user and vivid brand colors get muted; teams choose between rejecting dynamic color, accepting it only on neutral surfaces, harmonizing their own colors by at most fifteen degrees, or moving recognition to non-color signals.
Test yourself
- (Easy) According to Android's developer documentation, which Android version added dynamic color?
- (Easy) If Material Color Utilities' scoring component can't find a suitable color, which color does it return?
- (Medium) How does dynamic color affect brand recognition?
- (Medium) Why is "tone" in the HCT space defined directly as L*, and what's the consequence of this for the interface?
- (Hard) In a portrait, when the face covers more area than a red scarf, why might the source color end up being the scarf's red?
- (Hard) When photos of a light-skinned family and a dark-skinned family are used as wallpaper, why does the tonal spot scheme produce the same or a very close primary color, and why does this matter?
Answers
- Android 12 ("From wallpaper to interface").
- #4285F4; the fallback color the library calls Google blue ("How the source color is chosen").
- If an app accepts dynamic color, its accent colors come from the user's wallpaper, and the same app looks different on different phones; even when the brand color is given as the source, the tonal spot scheme brings its chroma down to 36 and pulls a vivid red toward a muted tone like #904A43. Brands can reject dynamic color, accept it only on neutral surfaces, harmonize their own colors by at most fifteen degrees, or move recognition to the logo, shape, and typeface ("The brand's loss of control").
- L* is a lightness measure arranged according to perception and is directly related to contrast; colors at the same lightness step give roughly the same contrast against white, whatever their hue. That's why a designer can guarantee the primary button's readability in advance, even for a source color they've never seen ("A whole palette from a single tone").
- Scoring looks not just at area but also at how far chroma is from the target value; the scarf, with its higher chroma, can outrank the face, which is larger but less saturated. The dark gray background, meanwhile, is eliminated from the start because its chroma falls below 5 ("How the source color is chosen").
- Human skin tones' HCT hues cluster in a narrow orange range, and the tonal spot scheme discards the source's lightness while fixing its chroma; #F1C7A9 and #8D5524 give the same primary color, #89511F. As a result, the interface's readability doesn't depend on the lightness of the skin of the people in the photo ("Example — a family photo as wallpaper").
Sources
- Android Developers, "Use the device colors (Dynamic Color)."
- material-foundation/material-color-utilities, project documentation (README).
- material-foundation/material-color-utilities, "score/score.ts," "scheme/scheme_tonal_spot," "blend/blend," and "dislike/dislike_analyzer" source files.
- The @material/material-color-utilities 0.3.0 npm package; this lesson's scheme and scoring calculations were made with this version.
Next step
UX-07 turns the uncertainty dynamic color leaves for the designer into a design principle: in a world where we don't know which colors the user will see, and some users can't even tell red from green, it asks how to mark an invalid form field without relying on color. UX-08, in turn, moves on to how these kinds of decisions get tested: it covers what automatic contrast checkers and vision simulators catch and miss, and why they can't replace testing with older and low-vision users.