Two colors, all the depth

The brief arrives as a sentence fragment: *hero image for the launch email, on-brand, by Thursday.* The photo is fine — a moody warehouse, a mountain ridge, a portrait with good light — but it is everybody's photo, full of colors that belong to the camera instead of the campaign. You could desaturate it and call it sophisticated, or you could run it through a duotone: every tone in the image re-rendered between two of your colors, the shadows wearing one and the highlights wearing the other. The structure of the photograph survives intact. The palette becomes yours.

Duotone is one of the oldest tricks in print — two inks instead of four, a halftone separated between them — and it has aged into one of the most recognizable styles on the modern web: streaming-service playlists, editorial features, conference sites, every indie band's cover art. The look is confident because it is constrained: two colors, full tonal range, no negotiation.

This maker does the whole job in the browser. Drop in a photo, pick or paste the two colors, and watch the live preview re-ramp as you move. What follows is what is actually happening to your pixels — the luminance math, the interpolation, and the reasons some color pairs sing while others sink.

What a duotone actually is

Strip the aesthetic language away and a duotone is a two-step pixel operation. Step one: collapse each pixel to a single brightness value — the same grayscale conversion any black-and-white photo uses. Step two: treat that brightness as a position along a ramp whose endpoints are your two colors, and emit the blend at that position. Dark pixels land near the shadow color, bright pixels near the highlight color, and the vast middle of the image spreads across every mixture between them.

The phrase worth holding onto is *tonal structure*. The grayscale step preserves everything about lightness — shape, shading, texture, depth of field — and throws away hue and saturation. The ramp step then re-dresses that structure in exactly two hues. That is why a good duotone still reads as a photograph rather than as a graphic: the skeleton is the camera's; only the flesh is painted.

It also explains what a duotone is not. It is not a color overlay — an overlay multiplies or screens one color over the image and leaves original hues partially intact. It is not a tint — tinting shifts hue while keeping each pixel's own lightness relationships muddied by the source colors. A duotone replaces the palette outright, which is precisely why it can take a random stock photo and make it look like art direction.

Making one, start to finish

  1. Load the photo

    Drag it in, click to browse, or paste from the clipboard. JPG, PNG, WebP, AVIF, GIF — the browser's decoder list is the supported list.

  2. Choose the two colors

    Start from the curated pairs if you are exploring, or paste hex codes straight into the shadow and highlight fields if the brand guide is open in the next tab. The preview re-renders on every change, instantly, at full resolution.

  3. Tune the feel

    A contrast slider re-spreads the input tones before mapping — useful for flat sources. A ramp midpoint slider biases where the shadow color hands off to the highlight, letting you push a portrait warmer or a landscape moodier without touching the colors themselves.

  4. Compare pairs side by side

    Pin up to three variants to compare — navy-on-amber against teal-on-coral against your exact brand pair. Choosing colors in isolation lies; choosing them side by side does not.

  5. Export

    PNG by default, transparency honored. JPG if the file is headed somewhere size-constrained. The filename keeps both hex codes so next Tuesday's you knows which variant this was.

The loop from color change to visual verdict is instant because the computation is trivial — a per-pixel multiply and add. All the time goes into the only genuinely hard part: picking the pair.

Luminance: the gray beneath the color

The first pass converts every pixel to brightness, and *how* you compute brightness visibly changes the result. The naive average — (R + G + B) / 3 — treats the three channels as equals, which human vision does not. Our eyes weigh green far more than blue, and standard grayscale conversion follows the Rec. 709 weights defined for HD video:

Y = 0.2126·R + 0.7152·G + 0.0722·B      // Y in 0..255

orange (255, 165,   0)  ->  Y = 54.2 + 118.0 + 0    = 172
navy   ( 18,  52,  86)  ->  Y =  3.8 +  37.2 + 6.2   =  47
yellow (255, 255,   0)  ->  Y = 54.2 + 182.4 + 0    = 237

Green contributes more than 71% of luminance, red about 21%, blue under 8%. Two consequences you can see in the preview: a lush green landscape becomes a bright, airy duotone while a night-blue cityscape sinks toward the shadow color; and a red-and-green scene that looks balanced in color has its red areas render noticeably darker than they appear. The luminance pass is honest to the eye, not to the channel values, and every good grayscale and duotone tool makes the same trade.

The output of this pass is a single number per pixel, and normalizing it to the 0–1 range gives `t` — the position along the color ramp. That number is the entire interface between your photograph and your palette, which is a pleasing amount of compression for an entire visual style to fit through.

The ramp between two colors, worked through

Step two is a straight line in RGB space between the shadow color and the highlight color. For a pixel sitting at ramp position `t`, each output channel is a linear interpolation:

C_out = C_shadow + t × (C_highlight − C_shadow)

shadow    = #1A237E  (26, 35, 126)
highlight = #FFCA28  (255, 202, 40)

t = 0.00 ->  (26, 35, 126)   pure shadow
 t = 1.00 ->  (255, 202, 40)   pure highlight

Run the arithmetic on a mid-tone pixel with Y = 128, so t = 128 / 255 = 0.502:

ChannelShadowHighlightInterpolationOutput
Red2625526 + 0.502 × (255 − 26) = 26 + 115.0141
Green3520235 + 0.502 × (202 − 35) = 35 + 83.8119
Blue12640126 + 0.502 × (40 − 126) = 126 − 43.283
One mid-gray pixel (Y = 128) mapped through the navy→amber ramp: output RGB(141, 119, 83), a warm olive.

Notice what the interpolation does with the blue channel: the shadow color has more of it than the highlight does, so blue *decreases* as tones brighten. Each channel moves in whatever direction the two endpoint colors dictate, which is why a duotone ramp is not a brightness fade but a genuine color journey — navy's coolness draining into amber's warmth as the light rises, passing through an unglamorous but structurally load-bearing olive on the way.

The midpoint slider in the tool bends this ramp without changing its endpoints: values of `t` below a bias point get squeezed toward shadow, above it stretched toward highlight. It is a remap of the ramp position before the interpolation runs — cheap to compute, disproportionately expressive in practice.

Run the full pipeline on three pixels from a real photo with this navy/amber pair — a bright sky highlight, a mid-tone wall, and a deep shadow in a doorway:

Source pixelRGB inLuminance YRamp tDuotone out
Bright sky240, 244, 2502430.95244, 194, 46
Mid wall128, 128, 1281280.50141, 119, 83
Doorway shadow18, 20, 24200.0844, 48, 119
Three tones through the full pipeline: luminance, normalization, interpolation. The wall's output is the olive computed above; the shadow stays recognizably navy.
Verifying the sky pixel by hand

Input

Y = 0.2126×240 + 0.7152×244 + 0.0722×250 = 51.0 + 174.5 + 18.1 = 243.6 → t = 0.955

Output

R = 26 + 0.955×229 = 245 · G = 35 + 0.955×167 = 194 · B = 126 + 0.955×(−86) = 44 → (245, 194, 44)
Verifying the doorway shadow

Input

Y = 0.2126×18 + 0.7152×20 + 0.0722×24 = 3.8 + 14.3 + 1.7 = 19.8 → t = 0.078

Output

R = 26 + 0.078×229 = 44 · G = 35 + 0.078×167 = 48 · B = 126 − 0.078×86 = 119 → (44, 48, 119)

Both hand checks land within a unit or two of the table values — the discrepancies are rounding at the half-step, nothing more. This is the entire algorithm: two multiplies and a weighted sum per pixel, repeated a few million times, faster than the preview can redraw.

The filter under the hood

Browsers ship a native primitive for exactly this transformation, and the maker uses it when available. An SVG filter can carry the whole pipeline in declarative form — the luminance collapse in an feColorMatrix, the two-color ramp in an feComponentTransfer:

<filter id="duotone">
  <feColorMatrix type="matrix"
    values="0.2126 0.7152 0.0722 0 0
            0.2126 0.7152 0.0722 0 0
            0.2126 0.7152 0.0722 0 0
            0      0      0      1 0"/>
  <feComponentTransfer>
    <feFuncR type="table" tableValues="0.102 1.000"/>
    <feFuncG type="table" tableValues="0.137 0.792"/>
    <feFuncB type="table" tableValues="0.494 0.157"/>
  </feComponentTransfer>
</filter>

The matrix rows are identical on purpose: every output channel receives the same luminance value, which is the grayscale step. The `tableValues` are the ramp endpoints, normalized to 0–1 — divide each channel of your two colors by 255. For the navy/amber pair: red runs 26/255 = 0.102 to 255/255 = 1.000, green runs 0.137 to 0.792, blue runs 0.494 down to 0.157. A `table` transfer with two values is by definition the linear interpolation between them, so the declarative filter computes precisely what the hand arithmetic above computed.

When the SVG path is unavailable, the tool falls back to a canvas ImageData loop implementing the same two equations directly. The two implementations agree to within a rounding step — there is only one correct answer here, and both roads reach it.

There is also an older compositing trick — multiply the grayscale with one color, screen it with the other — that approximates a duotone with blend modes alone. It is clever, it predates good filter support, and it is subtly wrong: multiply and screen bend the ramp per-channel rather than interpolating linearly, so the mid-tones drift off the line between your two colors. Modern tooling has no reason to accept the drift, and this maker does not.

Choosing pairs that work

The algorithm is deterministic; the taste is not. A few rules carry most of the weight:

  • Separate luminance above all. Shadow color dark, highlight color light. This single decision preserves the photo's depth; a pair like dusty rose and sage — both mid-brightness — produces a pancake.
  • Complementary pairs for energy, analogous pairs for calm. Navy-and-amber, teal-and-coral, purple-and-yellow: high hue distance reads bold and poster-like. Blue-and-teal, red-and-orange: neighboring hues read subdued and premium.
  • Let the subject pick the dominant color. Portraits flatter when skin tones land on the warmer color — put the warm hue at the end where faces actually fall in the tonal range, often the highlight.
  • Test against the worst case, not the best. Scroll your preview to the darkest region of the photo; if it vanishes into the shadow color, either lighten that color or lift the contrast slider.
  • Borrow proven pairs, then adjust. The curated presets are starting points with known-good luminance separation; pasting your brand hex over one end of a proven pair beats starting from scratch.

And a rule for the impatient: if two candidates are arguing in your head, make both, pin them side by side, and let the photo cast the deciding vote. The comparison view exists because this decision is made better with eyes than with theory.

Edges, and where the effect gives up

Honest limits, encountered in the order real users hit them:

  • Color-cast sources flatten. A photo that is 80% blue-toned — night streets, underwater, blue-hour landscapes — has most of its luminance values clustered low. The duotone maps that cluster to a narrow band of the ramp and the result reads as a poster of one color. The contrast slider exists for exactly this.
  • Already-gray sources are the easy case. The luminance pass is a no-op on a true grayscale image, so the ramp gets full range and maximum drama. Black-and-white photos make the best duotones, which the print world has known for a century.
  • Transparency passes through. Alpha is untouched by both passes; a logo duotone on transparent background exports as a transparent PNG with the two-color treatment intact.
  • Detail survives, texture survives, hue does not. If the original image's meaning depends on color — a ripeness cue, a chart's legend, a bruise, a stoplight — a duotone removes that meaning deliberately. Some information is not supposed to survive art direction.
  • Cling-wrap JPG sources show their seams. Compression artifacts sit in chroma, and chroma is exactly what gets discarded; what remains is the luminance shadow of the artifacts, usually invisible. Occasionally a very damaged file gets slightly noisier in flat areas, and the fix is a better source image.

Who makes duotones on purpose

Brand and marketing designers are the core audience — hero images, event pages, social templates, and the recurring need to make three different stock photos look like one campaign. A duotone in brand colors is the fastest reconciliation of *photographic* and *on-brand* that does not involve a photo shoot.

Editorial and publishing people use them for the same reason at print resolution: section headers, pull-quote backgrounds, cover variants. The two-ink heritage is not nostalgic for them — risograph and screen-print shops still quote two-color jobs, and previewing the separation before committing to plates is a genuine production step, not just an aesthetic one.

And a long tail of makers: playlist and podcast cover art, YouTube thumbnails that must survive both light and dark interfaces, t-shirt graphics, conference badges, zines. Anything that needs to look designed with two decisions instead of two hundred.

The color-family tooling continues around it: the Grayscale Image Converter is the neutral half-step, the Tints and Shades Generator builds the surrounding palette for your chosen pair, and the Image Color Extractor pulls candidate colors from the photo itself. The invert-colors tool covers the opposite-universe branch of tonal play when that is the mood you are chasing.

This maker versus ShapeFactory versus Photoshop

ApproachCustom color pairsFile handlingLive previewCost
This makerAny hex or HSL, plus presetsNever uploadedInstant, full resolutionFree, no account
ShapeFactory duotoneTheir curated gradient pickerUploaded to their serverYesFree tier with limits
Photoshop Gradient MapFull control, in a 900 MB appLocalYes, per adjustmentSubscription
CSS/SVG hand-codedFull control, every timeNo file — it is the mediumLive by natureFree, your afternoons
Four roads to a duotone, from paste-and-go to do-it-yourself.

ShapeFactory deserves its reputation — it proved a single page could own this niche, and its curated gradients are genuinely tasteful. The gaps are the ones that matter for daily work: uploads instead of local processing, a fixed palette library instead of exact brand hex codes, and a UX last refreshed in an era when duotone was the new thing rather than the standard thing.

Photoshop's Gradient Map is the reference implementation and remains unmatched inside a larger retouching workflow. The case for a browser tool is the 95% case: one image, two colors, done before the photo editor finishes its splash screen. Hand-coded SVG filters are for engineers who enjoy owning their own pipeline — and for them, the filter block above is a working starting point.

The photo stays on your machine

Both passes — luminance and ramp — run as canvas operations inside the tab. The file loads through the browser's image decoder, the pixels transform in memory, and the export encodes back to a local download. No upload endpoint exists in the page, which for unreleased product photography and client work under NDA is not a feature so much as the absence of a liability.

The local pipeline also keeps the feedback loop honest: because nothing round-trips through a server, a color change re-renders at full resolution with zero perceptible delay, and comparing three pinned variants is three canvas redraws rather than three queued uploads. Interactive design decisions need that immediacy; waiting for a render queue quietly kills the exploratory half of color picking.

As with everything in this cluster: load the page once, and it works with the network unplugged thereafter — planes, secure facilities, and the borrowed laptop at the conference booth included.

Duotone questions

What is the difference between a duotone and a grayscale image?

Grayscale renders every tone as some shade of neutral gray. A duotone keeps the exact same tonal structure — every pixel's brightness — but renders the ramp between two colors you choose instead of between black and white. Shadows take the first color, highlights take the second, and mid-tones land on blends of the two. Same skeleton, different wardrobe.

How do I pick two colors that work?

The rule that matters most: separate the colors in luminance. A dark color for shadows and a light color for highlights preserves the image's depth; two colors of similar brightness flatten everything into a banner of mud. After luminance separation, anything goes — complementary pairs read loud and energetic, analogous pairs read calm and editorial, and exact brand colors read on-message.

Can I use exact hex codes for my brand?

Yes — both color stops accept hex, RGB, or HSL input, and the output ramp is computed from the exact values. A brand duotone with your dark navy and brand amber at the two ends is two clicks away, which is the entire reason most people arrive at this page.

Does it work on portraits and photos of people?

Yes, and faces are where duotones shine — skin tones and features survive well because luminance carries all the structure. The effect leans editorial rather than photographic, and mid-tone-heavy portraits look especially good with a high-contrast color pair. Faces you need to anonymize are a different job; the pixelate and blur tools cover that.

Why does my duotone look flat and lifeless?

Almost always one of two causes. Either the two colors sit too close in brightness — the ramp has no tonal room to describe the image — or the source photo is low-contrast to begin with, so the tones cluster in a narrow band. Fix one of each: darken the shadow color or lighten the highlight, and pick a source image with real blacks and real whites.

Which formats can I load and export?

Load anything the browser decodes — JPG, PNG, WebP, AVIF, GIF. Export as PNG (lossless, transparency preserved) or JPG for smaller files. The color mapping itself never compresses anything; artifacts only enter at export if you choose JPG.

Is my image uploaded to a server?

No. The luminance pass and the color ramp both run in your browser on the canvas, and the file never crosses the network. Client work, unreleased product shots, and personal photos stay on your machine — the network panel confirms it in real time.

Pair it with these

Color-adjacent stops: Grayscale Image Converter for the neutral version, Tints and Shades Generator to build out the palette around your pair, and Image Color Extractor to mine the photo for its own best colors.

Sources and further reading

We cite primary sources (standards bodies, government agencies, official documentation) wherever they exist so you can verify every number on this page yourself.