Putting a Procreate Flourished Bird on the Web: Blend Modes vs Alpha
A small rendering note from placing a Procreate ornamental bird on a theme-aware homepage: why blend modes looked better, and why the dark image flashed.
The hero image on this site is a flourished bird I drew in Procreate, in the spirit of Ornamental Penmanship. Technically it is a monochrome brush drawing exported from a white canvas, with many anti-aliased gray pixels around the strokes.
Putting that image on a light and dark website surfaced two separate problems: the transparent PNG looked a little foggy around the edges, and the dark version briefly flashed with the wrong background during hydration.
Why the Dark Image Flashed
My first version switched the src and blend style after next-themes mounted on the client. That meant the server initially rendered the light image, then the browser swapped in the dark image after hydration.
The image itself was not the only changing piece. mix-blend-mode depends on the pixels behind the image, and Framer Motion had introduced a transform layer around the hero. During the swap, the browser briefly combined the old bitmap, the new style, and an isolated compositing context. The result was a visible rectangle instead of a clean transition.
The fix was simple and intentionally boring: render both images on the server, preload both, and let CSS decide which one is visible.
<Image className="block dark:hidden" priority src="/images/hzjwebsitefront.png" alt="" />
<Image className="hidden dark:block" priority src="/images/hzjwebsitefront_dark.png" alt="" />There is no client-side src swap anymore. Theme changes become a class change on <html>, so the browser already has both bitmaps.
Why Naive Transparency Looked Worse
The second issue was subtler. If you remove the white background with a hard threshold, the anti-aliased edge pixels become opaque gray pixels.
That is not what the drawing meant. Those gray pixels are really "black ink with less pressure over a white canvas." They should become semi-transparent ink, not solid gray paint.
For this kind of monochrome drawing, luminance can become alpha:
lum = image.convert("L")
alpha = ImageChops.invert(lum)Bright canvas pixels become transparent. Dark stroke pixels stay opaque. Mid-tone edge pixels become partially transparent, which preserves the brush pressure instead of freezing it into a gray halo.
The Final Compromise
The light theme still uses the original white-background image with mix-blend-mode: multiply, because it keeps the ink weight richer on a paper-like background. The dark theme uses the luminance-alpha version, because that avoids the rectangle flash and gives the browser a clean transparent asset.
The lesson was useful beyond this one bird: image optimization is not always "make it transparent." Sometimes the original canvas, the blend mode, and the browser's compositing model are part of the artwork.

