Home (All Topics) → Technology → Software → AI-Created Software
The following is AI-generated (aka vibe-coded) software. The software is specific to my needs and my system and may not work for you or your system. Support is limited, but if you report a bug I'll certainly look into it. All code is provided in a zip file for you to review should you choose.
A desktop tool for blending an after image into a before image, weighted by a grayscale mask and scaled by a single slider. White areas of the mask take the after image, black areas keep the before image, and grays mix the two. At 0% the result is the pure before image; at 100% it is the full mask-weighted blend.
It is built for comparing and dialing in edits: retouching, relighting, compositing tests, or any case where you want an edit applied only in some places and only by some amount.
python3-tk)Tested with Python 3.12 and Pillow 12.
The app is a single script, so there is nothing else to install. If a file named image-blender-with-mask.ico sits next to the script, it is used as the window icon.
Every option is named and takes a value, written --name VALUE or --name=VALUE, in any order.--help lists them with their defaults. Script launchers that read the options (such as Scriptary) list --working-folder-path first.
| Option | What it does |
|---|---|
--working-folder-path FOLDER |
The folder for the settings file and temporary files, created if needed. Blank or not given: the folder the app’s script is in. |
A mistake on the command line, such as an unknown option or a folder that can’t be created, is reported with “Use –help for usage.” and exit status 2. Started without a console (pythonw.exe), the message is shown in a message box instead.
If you don’t have a mask, choose All white in the Mask switch to apply the after image uniformly, or use Generate from difference in Mask tools.
Everything is in two strips above the image:
Hover over a control for what it does and its keyboard shortcut. A button is grayed out until it can act: the exports need a before and after image and a mask (or a mask override), Invert needs a loaded mask, and Recent needs a project opened before.
The after image and mask are resampled to the before image’s size, so they don’t need to match it exactly. If the after image has a different aspect ratio, it is stretched to fit and a warning appears in the slot strip. Photos are turned upright according to their camera orientation.
Supported formats are PNG, JPEG, TIFF, WebP and BMP.
With tkinterdnd2 installed, you can drop files in two ways:
before, after, mask or matte go to the matching slot; the rest fill the remaining empty slots in order. If every slot is already full, you’re asked which slot to replace.Dropping a project file opens that project.
The View switch sets the base scale:
| Mode | Shows |
|---|---|
| Fit | The whole image, scaled to the window |
| 1:1 before | The before image at actual size |
| 1:1 after | The after image at actual size |
| 2× | The smaller of the two images at double size |
The mouse wheel, the − and + buttons beside the View switch, and the + and − keys zoom from any mode, keeping the point under the cursor in place. 0 returns to Fit. Drag with the mouse to pan.
The Show switch chooses what the image area shows: the Blend, a Wipe with the before image on the left of a divider and the blend on its right (W turns it on and off), or the Mask on its own, in gray. Holding B shows the pure before image and holding M the mask, for as long as the key is down, whatever Show is set to.
In wipe view, drag the divider to move it; drag anywhere else to pan.
The Mask switch chooses between the Loaded mask, All white and All black. Invert beside it (I) swaps where the after image is applied, the same as Invert in Mask tools. Linear light blends in linear light rather than on the files’ encoded values, for smoother mixes between very different colors. The overlay color and opacity are set in Mask tools and never affect saved output.
Mask tools offers:
| Setting | Effect |
|---|---|
| Channel | Which channel of the mask file is used |
| Invert | Swaps where the after image is applied |
| Black / white point | Stretches the mask’s tonal range |
| Threshold | Turns the mask into a hard-edged selection at a chosen level |
| Feather | Blurs the mask edge by the given radius in pixels |
The Mask tools window opens where you last closed it. While you drag a Mask tools slider, the preview updates at screen resolution. The full-resolution mask is rebuilt in the background a moment after you stop. Save mask writes the adjusted mask at its own resolution as a 16-bit PNG or TIFF.
Every mask change, including loading a new mask or generating one, can be undone with Undo (at the right end of the view controls) or Ctrl+Z, and redone with Redo, Ctrl+Shift+Z or Ctrl+Y. The buttons are grayed out when there is nothing to undo or redo. A continuous slider drag counts as one step.
The last 100 changes are kept, until you close the app.
The bar below the slider shows the values under the cursor: image coordinates, before and after RGB, the mask value and the blend weight it produces, and the resulting RGB. These are computed from the full-resolution sources, whatever the zoom level.
Turn on Watch files to reload any source that changes on disk, such as a mask you are painting in another editor.
Sources are checked once a second, on a separate thread, so a sleeping or network drive doesn’t freeze the window. A file that disappears or can’t be read is reported in the slot strip, and the app retries on the next check.
Ctrl+R reloads all sources on demand. The slider, zoom and mask settings are kept.
Save project asks for a folder and creates a sub-folder inside it named after the before image. That sub-folder contains:
<name>.ibwmproj, plain JSON)generated_mask.png, if the mask was generated rather than loaded from a fileresult.png, the rendered blend at the before image’s sizeHover over Save project or Load project for a summary of the files each writes or expects.
Projects restore the images, the slider value, the zoom, all mask settings, the mask override, the overlay settings and the linear-light option. Recently opened projects are listed under Recent, which also has Clear recent.
Turn on Portable when you want to move or share a project. Linked projects stay small but depend on the original files staying where they are. On systems where symbolic links aren’t allowed, hard links are used instead.
The title bar shows • while there are unsaved changes, and the app offers to save before you close it or open another project.
Export image (Ctrl+E) saves the blend at the before image’s size, the after image’s size, or the current zoomed-out view size.
| Format | Notes |
|---|---|
| PNG | Lossless |
| JPEG | Adjustable quality (95 to start with), without chroma subsampling |
| TIFF | Lossless, LZW-compressed |
| WebP | Adjustable quality (95 to start with) |
| BMP | Uncompressed; no color profile |
The file extension you type decides the format, so result.jpg is always saved as a JPEG. Every export is written to a temporary file beside the target first and then put in its place, so a failed export never leaves a half-written file over an earlier one. The before image’s ICC color profile is embedded in every format except BMP, and its camera data (EXIF) in PNG, JPEG and WebP, with the orientation reset since the result is already upright.
Export sweep (Ctrl+Shift+E) renders a series of slider values between a start and end percentage. It can export:
sweep_000.png, sweep_001.png… files in a folder. If the folder already contains frames from an earlier sweep, you’re offered the chance to delete them first.Easing controls how the slider values are spaced across the frames: linear, ease in, ease out, or ease in-out.
Ping-pong plays the sweep forward and then back.
A progress bar tracks rendering, and Cancel stops the export. Animations hold every frame in memory while they are encoded, so you’re warned first if that would need more than about 2 GB.
On macOS, use Cmd in place of Ctrl.
| Keys | Action |
|---|---|
| Ctrl+1 / Ctrl+2 / Ctrl+3 | Load before / after / mask |
| Ctrl+O | Load project |
| Ctrl+S | Save project |
| Ctrl+E | Export image |
| Ctrl+Shift+E | Export slider sweep |
| Ctrl+R | Reload sources |
| Ctrl+Z | Undo mask change |
| Ctrl+Shift+Z or Ctrl+Y | Redo mask change |
| Left / Right | Slider −1% / +1% |
| Shift+Left / Shift+Right | Slider −10% / +10% |
| Home / End | Slider to 0% / 100% |
| + / − | Zoom in / out |
| 0 | Fit to window |
| Hold B | Show the pure before image |
| Hold M | Show the mask on its own |
| O | Toggle the mask overlay |
| W | Toggle the wipe comparison |
| I | Invert the mask |
Each button’s tooltip shows its shortcut. Single-key shortcuts act in the main window only. No shortcut acts while you are typing in a field (there, Ctrl+Z undoes your typing) or while a dialog such as Export is open. The Ctrl shortcuts work the same with Caps Lock on.
Every color in the interface is a neutral gray, with the same red, green and blue values, so nothing next to the images tints how you see them. The only colors on screen come from the images themselves and the mask overlay. A chosen option is shown filled in light gray, and anything that needs your attention, such as a missing source file, is shown in brighter text with a ⚠ sign.
Everything is drawn to match the display scaling set in Windows or Linux, not only the text: buttons, spacing, sliders, badges and the wipe divider. At 150% they are all half as large again as at 100%. On Windows the app tells the system it handles scaling itself, so text is drawn sharply at the screen’s own resolution instead of being stretched. On macOS, sizes follow the system’s points.
When the window is narrow, the file buttons use shorter labels, and when that isn’t enough, the exports and the project buttons move onto a second row. The view and mask controls take as many rows as they need, up to three: the first row keeps the mask summary, and Undo and Redo at its right end; and the key hint under the slider is shortened, its full text in a tooltip. At the smallest window size (700 × 600 at 100% scaling) nothing is cut off.
Window size and position, where Mask tools and the other popup windows were last closed, recent projects, and preferences such as portable export and file watching are stored in image-blender-with-mask.ini in the working folder (see Command line), and saved when the app closes.
If a saved window position is no longer on a monitor (on Windows, each monitor is checked), the window opens at its saved size in a default position instead; a popup window opens centered over the main window.
Until November 2026 the default working folder was %APPDATA%\image-blender-with-mask on Windows (~/Library/Application Support/image-blender-with-mask on macOS, ~/.config/image-blender-with-mask elsewhere); now it is the app’s own folder. The first time the app starts with no settings of its own there, it copies the settings file from that old folder, which is then left as it was and can be deleted. The oldest versions kept a settings file of another layout next to the script; that one is converted in place the same way. --settings-file-path is gone: use --working-folder-path instead.
If the settings can’t be read (for example, after editing the file by hand), the app starts with the defaults; a value it doesn’t understand falls back to its default. If the working folder can’t be created, the app runs without saving settings and says so on the console. If the settings file can’t be written, the app says so in a message box and carries on.
Drag and drop doesn’t work. Install tkinterdnd2 with pip install tkinterdnd2 and restart the app. Without it, the drop hint is not shown and files must be opened with the Open buttons.
“Tkinter is not available”. Your Python was built without Tk; the app says so and exits. On Debian or Ubuntu run sudo apt install python3-tk; on other systems, use the python.org installer or your package manager’s Tk package.--help works without it.
My settings are gone after updating. Settings now live in the working folder, by default the app’s own folder (see Settings file). They are copied from the old folder only when the working folder has no settings yet; if you started the new version with --working-folder-path pointing somewhere else first, copy the values across by hand, or run with --working-folder-path set to the old folder.
The taskbar shows the app as Python, or a pinned shortcut opens a second button. On Windows the app now has its own taskbar ID, so a shortcut pinned by an earlier version belongs to Python. Unpin it, start the app, and pin its own button again.
A linked project shows missing files. The original source files were moved or deleted. Restore them, or re-save the project with Portable turned on while the files are still available.
The window opens smaller than you left it, once, after updating. On Windows with display scaling, versions before the app became DPI-aware saved the window size in scaled-down units. Resize or maximize the window once and the new size is remembered.
The mask looks inverted or too faint. Check the source channel and the invert setting in Mask tools, and try the black and white points. Holding M shows exactly the mask that is being applied.
The tests use pytest and drive the real window on images they make themselves; they don’t touch your files or settings.
They need a display: without one (on Linux), they start Xvfb and openbox themselves if installed. To test at 150% display scaling, start Xvfb :98 -screen 0 3000x1800x24 -dpi 144 and run DISPLAY=:98 python -m pytest.
They cover the image and mask functions without a window (invert, levels, threshold, feather, blending in both modes, 16-bit and alpha masks, EXIF orientation, project files, a failed export leaving the old file), blending in the window (the slider, the mask, a 16-bit mask kept at full precision), a file that fails to open, export (quality, chroma subsampling, the color profile and camera data), projects (a round trip, a damaged project file left alone, and the Save and Load project tooltips naming the files), settings (saved and restored, a hand-edited file, settings from the old folder and the oldest layout taken over once, a failed save reported), popup windows reopening where they were closed, the command line (with --working-folder-path first and the script’s folder as the default), the neutral-gray styles, buttons and switches at one height, Export as the one solid button, the crosshair over the image and scrollbars shown only when needed, controls fitting at the smallest window, the switches with real clicks, Ctrl shortcuts while typing and with Caps Lock on, the hold keys waiting while a dialog is open, file watching asking the disk off the main thread (a changed file reloaded, a missing one reported), the Undo and Redo buttons, every former menu command as a working button or switch, both strips wrapping at three window widths, tooltips (one for a whole file cell, above a control at the bottom of the window, none when blank), closing without leftover jobs, and the icon. The 38 tests took about 9 to 11 seconds, and all passed at 100% and at 150% display scaling, with Python 3.12, Pillow 13.0, NumPy 2.5 and pytest 9.2 under Linux.


Leave a Reply