How it works

The image isn't changing. The background is.

A dark mode illusion image is an ordinary PNG. There's no script in the file, no animation and no device detection. All of the work happens in the alpha channel, and the viewer's own app does the rest without knowing it's been enlisted.

01

Why does an image look different in dark mode?

Because the app paints it over a different colour. When a client draws a partially transparent image, it blends each pixel with whatever is behind it. Light themes put white back there. Dark themes put black. For a pixel with straight colour C and alpha a, over a background B, the result is:

out = a·C + (1 − a)·B

Set B = 0 and you get what dark-mode users see: a·C. Set B = 1 and you get what light-mode users see: a·C + 1 − a. Two different outputs, one file, and the file never had to do anything.

02

How do you calculate the alpha channel for a dark mode illusion image?

Solve two compositing equations at once, per pixel. Say you want dark mode to show image D and light mode to show image L. That's two equations per pixel:

a·C = D            (over black, B = 0)
a·C + 1 - a = L    (over white, B = 1)

Substitute the first into the second and a closed form falls straight out: alpha equals one minus L plus D, and colour equals D divided by alpha.

a = 1 - L + D
C = D / a

Which is a complete answer — with one condition attached. Alpha has to land between 0 and 1, and that only happens when D ≤ L at every single pixel. The dark-mode image must be darker than the light-mode image everywhere. Call it the D ≤ L constraint: it is the entire cost of the technique, and every design decision in a dark-mode illusion generator follows from it. The two methods below pay it in different currencies.

Cite this:Duality, “How dark-mode illusion images work,” https://duality.drummerduck.com/how-it-works#alpha-solve

03

What is blend mode, and why does it look washed out?

The direct approach is to force the constraint to hold. Squash the light image up into the top half of the tonal range and the dark image down into the bottom half. Now D ≤ L is guaranteed and the solve is exact at every pixel — no texture, no artefacts, a completely smooth result.

The bill arrives as contrast. Each image only ever gets part of the range, so your light-mode picture can never go properly black and your dark-mode picture can never go properly white. Everything reads a little washed out. The Dark ceiling and Light floor controls in the studio are you deciding how to split that range between them.

Blend · light mode
Blend · dark mode
04

Why do dark mode illusion images have a visible grid or weave?

Because of how the better method avoids that contrast loss. Instead of asking every pixel to carry both images at half strength, it hands each pixel to one image and lets it go all the way.

A mask splits the canvas into two sets. Light-carrier pixels are asked to show the light image over white and pure black over black — substitute D = 0 and you get a = 1 − L, C = 0. Dark-carrier pixels do the mirror image: show the dark image over black and pure white over white, so L = 1, giving a = D and C = 1.

light carrier ->  a = 1 - L,  C = 0    (dark pixel, partial alpha)
dark  carrier ->  a = D,      C = 1    (white pixel, partial alpha)

Every pixel now keeps the full 0–1 range. The other image's pixels simply read as background — white on white, black on black — and your eye averages them away at normal viewing distance. You lose brightness rather than contrast, which is a much better trade for photographs.

The catch is that the mask is still physically there. Zoom into one of these images and you'll find a fine weave running through it. That texture isn't a filter or a stylistic choice — it's the seam where the two pictures are stitched together.

Interlace · light mode
Interlace · dark mode
05

Which mask pattern should you use?

Any mask works, so the pattern becomes an aesthetic decision. A 1px checkerboard disappears at full size. A 4px halftone is unmistakably deliberate. Scanlines read retro; crosshatch reads like engraving. Push the balance slider off 50/50 and one image gets more pixels than the other — useful, because most of X is on dark mode.

2px
50% light
Checkerboardchecker

Classic 1px alternation. The tightest, most invisible weave.

Bayer 4×4bayer4

Ordered dither. Soft, film-grain-ish texture.

Bayer 8×8bayer8

Finer ordered dither. Best for photos with big flat areas.

Scanlineslines

Parallel lines at any angle. Very legible, very retro.

Crosshatchcrosshatch

Two line sets crossing. Reads like engraving.

Halftone dotsdots

Round dot grid. Newsprint feel.

Ringsrings

Concentric rings from centre. Hypnotic on portraits.

Random noisenoise

Stable hashed noise. Hides the mask best, grainiest.

06

Does a layer have to be a photo?

No — either half can be typed text or a flat fill instead of an image. Text is where the technique stops being a curiosity and starts being a joke delivery mechanism: the setup is visible to everyone, and the punchline is reserved for whoever happens to have dark mode on.

  • Photo in light, caption in dark
  • Caption in light, photo in dark
  • Two photos, no text at all
  • One photo and a flat fill, so it only shows up in one theme
Light mode
Dark mode
07

Can you use colour photos in a dark mode illusion image?

Yes, but greyscale is the safe setting and here's why. Alpha is a single number shared by all three channels, which means a saturated pixel can't be solved cleanly for both themes at once. Interlace mode handles a light-carrier pixel by setting alpha from the darkestchannel, so any colour above that floor leaks a faint tint into the other theme. It's usually subtle, and it's always proportional to saturation.

That's why the saturation slider defaults to zero. Greyscale leaks nothing at all. Turn it up when the colour is worth a little ghosting, and watch the fidelity readout in the studio — it's measuring exactly this.

08

Why does my dark mode illusion image not work on Twitter/X?

Almost always one of three causes, and all three are avoidable:

  • X re-encoded your PNG to JPEG.JPEG has no alpha channel, so a re-encode destroys the illusion outright rather than degrading it. X keeps PNGs under about 5 MB — the studio measures the real encoded size and warns you before you cross it.
  • You're looking at a timeline thumbnail. Downscaling averages neighbouring pixels, and a 1px mask averages to flat grey. The full-size view is always correct, but if the timeline preview matters to you, use a larger cell size.
  • The viewer is on X's Dim theme. The maths assumes pure white and pure black. Dim is neither, so both images show through faintly at once. The studio previews Dim, Slack and iOS surfaces so you can check before posting.

Slack, Discord, iMessage, Notion and any web browser all preserve the effect. Instagram never will — it re-encodes everything to JPEG. The full table is on where it works.

09

Every knob the effect actually has

The illusion lives or dies on tone, and most images need a nudge before they survive both backgrounds. So the controls that matter are all exposed rather than hidden behind a single “generate” button.

Two render methods

Interlace for full contrast with a visible weave, or Blend for a completely smooth image that trades away tonal range.

Eight mask patterns

Checkerboard, two Bayer dithers, scanlines, crosshatch, halftone dots, rings and hashed noise — each with cell size and angle.

Per-layer tone

Brightness, contrast, gamma, saturation and invert on each half independently, because one photo is almost never too bright and too dark at once.

Image, text or flat fill

Each layer takes an upload, typed text with full size and weight control, or a solid colour to hide that half entirely.

Balance control

Most of X runs dark mode. Push the mask balance toward the dark image and the majority gets the sharper half.

Private by default

Decoding, compositing and PNG encoding happen on your machine. Share link is opt-in and only sends the final PNG — source photos never leave the tab.

Common questions

Do my images get uploaded anywhere?

Not by default. Decoding, compositing and PNG encoding all run in your browser. Download and Copy never leave this device. The only time anything is sent is if you click Share link — that uploads the final PNG only (not your source photos) so you get a public page with a working link preview. Cancel that dialog and nothing is stored.

Why does my photo look washed out?

Almost always tone. The light-mode image needs to sit bright and the dark-mode image needs to sit dark, so a photo that's mid-grey throughout has nowhere to go. Drop the gamma on the light layer and raise it on the dark one, then add contrast to both.

Can I use two colour photos?

Yes, with a caveat. Alpha is shared across the colour channels, so saturation always leaks a little into the other theme. Blend mode holds colour better than interlace. If the result looks muddy, pull saturation down before you touch anything else.

Does this work on Instagram?

No. Instagram re-encodes everything to JPEG, which discards the alpha channel. Anywhere that guarantees a JPEG is a dead end for this technique.

Why does the file get so large?

PNG compresses flat regions well and noisy ones badly, and a per-pixel mask is about as noisy as an image gets. Larger cell sizes compress much better than 1px ones. If you're near the limit, raise the cell size before you shrink the image.

Is this the same as those "hidden image" QR tricks?

No — those rely on the viewer squinting or moving. This is deterministic compositing. Two viewers on two themes get two genuinely different images out of identical bytes.

That's the whole trick.

It takes about twenty seconds to make one now that you know what the sliders are doing.

Open the studio